วิธีการเรียกฟังก์ชั่นการโหลดด้วย React useEffect เพียงครั้งเดียว


210

useEffect React เบ็ดจะทำงานที่ผ่านมาในการทำงานในทุกการเปลี่ยนแปลง สิ่งนี้สามารถปรับให้เหมาะสมเพื่อให้มันโทรเฉพาะเมื่อคุณสมบัติที่ต้องการเปลี่ยนไป

จะเกิดอะไรขึ้นถ้าฉันต้องการเรียกใช้ฟังก์ชันการเริ่มต้นcomponentDidMountและไม่เรียกมันอีกครั้งจากการเปลี่ยนแปลง สมมติว่าฉันต้องการโหลดเอนทิตี แต่ฟังก์ชั่นการโหลดไม่ต้องการข้อมูลใด ๆ จากส่วนประกอบ เราจะทำสิ่งนี้โดยใช้useEffectเบ็ดได้อย่างไร

class MyComponent extends React.PureComponent {
    componentDidMount() {
        loadDataOnlyOnce();
    }
    render() { ... }
}

ด้วย hooks นี้อาจมีลักษณะเช่นนี้:

function MyComponent() {
    useEffect(() => {
        loadDataOnlyOnce(); // this will fire on every change :(
    }, [...???]);
    return (...);
}

คำตอบ:


396

หากคุณต้องการเรียกใช้ฟังก์ชันที่กำหนดให้useEffectหลังจากสร้างการแสดงผลครั้งแรกคุณสามารถกำหนดให้อาร์เรย์ว่างเปล่าเป็นอาร์กิวเมนต์ที่สอง

function MyComponent() {
  useEffect(() => {
    loadDataOnlyOnce();
  }, []);

  return <div> {/* ... */} </div>;
}

27
อีกทางเลือกหนึ่งถ้ามี params ที่คุณใช้เพื่อดึงข้อมูล (เช่น id ผู้ใช้) คุณสามารถส่ง id ผู้ใช้ในอาร์เรย์นั้นและหากมีการเปลี่ยนแปลงส่วนประกอบจะดึงข้อมูล กรณีการใช้งานหลายอย่างจะทำงานเช่นนั้น
trixn

4
อ๋อ ... เพิ่มเติมเกี่ยวกับการกระโดดเป็นเอกสารที่นี่: reactjs.org/docs/...
Melounek

ดูเหมือนว่าคำตอบที่ง่ายที่สุด แต่ ESLint บ่น ... ดูคำตอบอื่น ๆ ในstackoverflow.com/a/56767883/1550587
Simon Hutchison

เพียงแค่โหลด loadDataOnlyOnce ลงในอาร์เรย์การพึ่งพา ใช้งานได้หรือไม่
jpmarks

88

TL; DR

useEffect(yourCallback, []) - จะทริกเกอร์การโทรกลับหลังจากการเรนเดอร์ครั้งแรกเท่านั้น

คำอธิบายโดยละเอียด

useEffectเรียกใช้ตามค่าเริ่มต้นหลังจากการแสดงผลทุกครั้งขององค์ประกอบ

เมื่อวาง useEffectองค์ประกอบของคุณคุณจะบอก React ว่าคุณต้องการเรียกใช้ callback เป็นเอฟเฟกต์ React จะเรียกใช้เอฟเฟกต์หลังจากแสดงผลและหลังจากทำการอัปเดต DOM

หากคุณผ่านการติดต่อกลับ - การติดต่อกลับจะทำงานหลังจากการแสดงผลแต่ละครั้ง

หากผ่านอาร์กิวเมนต์ที่สอง (อาร์เรย์) React จะเรียกใช้การเรียกกลับหลังจากการเรนเดอร์ครั้งแรกและทุกครั้งที่องค์ประกอบหนึ่งในอาเรย์เปลี่ยนไป ตัวอย่างเช่นเมื่อวางuseEffect(() => console.log('hello'), [someVar, someOtherVar])- โทรกลับจะทำงานหลังจากการแสดงครั้งแรกและหลังจากการแสดงผลใด ๆ ที่หนึ่งsomeVarหรือsomeOtherVarมีการเปลี่ยนแปลง

โดยผ่านอาร์กิวเมนต์ที่สองเป็นอาร์เรย์ว่างปฏิกิริยาจะเปรียบเทียบหลังจากที่แต่ละเรนเดอร์เรนเดอร์และจะไม่เห็นอะไรเลยดังนั้นจึงเรียกการเรียกกลับหลังจากการเรนเดอร์ครั้งแรกเท่านั้น


69

useMountEffect hook

การเรียกใช้ฟังก์ชั่นเพียงครั้งเดียวหลังจากการเมาท์ส่วนประกอบเป็นรูปแบบทั่วไปที่มันแสดงให้เห็นถึงเบ็ดของมันเองที่ซ่อนรายละเอียดการใช้งาน

const useMountEffect = (fun) => useEffect(fun, [])

ใช้ในองค์ประกอบการทำงานใด ๆ

function MyComponent() {
    useMountEffect(function) // function will run only once after it has mounted. 
    return <div>...</div>;
}

เกี่ยวกับ useMountEffect hook

เมื่อใช้useEffectกับอาเรย์อาร์กิวเมนต์ที่สอง React จะเรียกใช้การเรียกกลับหลังจากติดตั้ง (การเรนเดอร์เริ่มต้น) และหลังจากค่าในอาเรย์มีการเปลี่ยนแปลง เนื่องจากเราผ่านอาร์เรย์ว่างเปล่ามันจะทำงานหลังจากติดตั้งเท่านั้น


19
ฉันชอบคำตอบของคุณอย่างมากเนื่องจากกฎ ESLint "react-hooks / exhaustive-deps" จะล้มเหลวเสมอในรายการการพึ่งพาที่ว่างเปล่า และตัวอย่างเช่นเทมเพลต create-react-app ที่มีชื่อเสียงจะบังคับใช้กฎนั้น
Dynalon

1
เห็นด้วยอย่างสมบูรณ์กับ @Dynalon นี่ควรเป็นโซลูชันที่ได้รับการยอมรับเนื่องจากไม่ได้รบกวนกฎ
ESLint

ขอบคุณ @Dynalon และ Mikado68 :-) การตัดสินใจเป็นสิทธิพิเศษของ OP ฉันได้รับแจ้งเกี่ยวกับความคิดเห็นของคุณ แต่ OP ไม่ได้ คุณสามารถแนะนำให้เขาด้วยการแสดงความคิดเห็นโดยตรงกับคำถาม
ปลาคาร์พเบ็น

2
ตอนนี้คุณสามารถใช้useMountเมื่อฟังก์ชั่นเอฟเฟกต์ของคุณต้องการอะไรบางอย่างจากอุปกรณ์ประกอบฉาก แต่ไม่จำเป็นต้องเรียกใช้อีกครั้งแม้ว่าค่านั้นจะเปลี่ยนไปโดยไม่มี linter warnig:useEffect(()=>console.log(props.val),[])จะไม่มีคำเตือนการพึ่งพา แต่useMount(()=>console.log(props.val))จะไม่ทำให้เกิดการเตือน ฉันไม่แน่ใจว่าจะมีปัญหากับโหมดการทำงานพร้อมกันหรือไม่
HMR

1
ฉันชอบสิ่งนี้ :) เหตุผลเดียวกับสถานะก่อนหน้านี้; กฎ ESLint จะไม่บ่นเกี่ยวกับกฎนี้รวมทั้งชื่อที่เข้าใจง่ายกว่าอาเรย์ที่ว่างเปล่า
Frexuz

20

useEffectอาเรย์ว่างเป็นอาร์กิวเมนต์ที่สองที่จะ สิ่งนี้จะบอก React อย่างมีประสิทธิภาพโดยอ้างถึงเอกสาร :

สิ่งนี้บอกว่าปฏิกิริยาของคุณไม่ได้ขึ้นอยู่กับค่าใด ๆ จากอุปกรณ์ประกอบฉากหรือรัฐดังนั้นจึงไม่จำเป็นต้องเรียกใช้อีกครั้ง

นี่คือตัวอย่างข้อมูลที่คุณสามารถเรียกใช้เพื่อแสดงว่าทำงานได้:

function App() {
  const [user, setUser] = React.useState(null);

  React.useEffect(() => {
    fetch('https://randomuser.me/api/')
      .then(results => results.json())
      .then(data => {
        setUser(data.results[0]);
      });
  }, []); // Pass empty array to only run once on mount.
  
  return <div>
    {user ? user.name.first : 'Loading...'}
  </div>;
}

ReactDOM.render(<App/>, document.getElementById('app'));
<script src="https://unpkg.com/react@16.7.0-alpha.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16.7.0-alpha.0/umd/react-dom.development.js"></script>

<div id="app"></div>


4

ฉันชอบที่จะกำหนดmountฟังก์ชั่นมันเล่นกล EsLint ในทางเดียวกันuseMountและฉันคิดว่ามันอธิบายตนเองได้มากกว่า

const mount = () => {
  console.log('mounted')
  // ...

  const unmount = () => {
    console.log('unmounted')
    // ...
  }
  return unmount
}
useEffect(mount, [])

-2

เคล็ดลับคือ useEffect ใช้พารามิเตอร์ตัวที่สอง

พารามิเตอร์ที่สองคืออาร์เรย์ของตัวแปรที่ส่วนประกอบจะตรวจสอบเพื่อให้แน่ใจว่ามีการเปลี่ยนแปลงก่อนที่จะเรนเดอร์ใหม่ คุณสามารถใส่อะไรก็ได้และระบุว่าคุณต้องการที่นี่เพื่อตรวจสอบ

หรือไม่ใส่อะไร:

import React, { useEffect } from 'react';

function App() {
  useEffect(() => {

    // Run! Like go get some data from an API.

  }, []); //Empty array as second argument

  return (
    <div>
      {/* Do something with data. */}
    </div>
  );
}

เพื่อให้มั่นใจว่า useEffect จะทำงานเพียงครั้งเดียว

หมายเหตุจากเอกสาร:

หากคุณใช้การปรับให้เหมาะสมนี้ตรวจสอบให้แน่ใจว่าอาร์เรย์มีค่าทั้งหมดจากขอบเขตองค์ประกอบ (เช่นอุปกรณ์ประกอบฉากและสถานะ) ที่เปลี่ยนแปลงตลอดเวลาและมีการใช้งานโดยเอฟเฟกต์ มิฉะนั้นรหัสของคุณจะอ้างอิงค่าเก่าจากการแสดงก่อนหน้านี้


4
หากคัดลอก / วางของคุณอย่างแท้จริงจากเคล็ดลับ CSS อย่างน้อยคุณสามารถทำได้คือเครดิตแหล่งที่มาของคุณ css-tricks.com/run-useeffect-only-once
burtyish
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.