Cách gọi chức năng tải với React useEffect chỉ một lần


205

Các useEffect Phản ứng móc sẽ chạy thông qua chức năng thay đổi mỗi ngày. Điều này có thể được tối ưu hóa để cho phép nó chỉ gọi khi các thuộc tính mong muốn thay đổi.

Điều gì xảy ra nếu tôi muốn gọi một hàm khởi tạo từ componentDidMountvà không gọi lại nó khi thay đổi? Giả sử tôi muốn tải một thực thể, nhưng chức năng tải không cần bất kỳ dữ liệu nào từ thành phần. Làm thế nào chúng ta có thể làm điều này bằng cách sử dụng useEffectmóc?

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

Với móc này có thể trông như thế này:

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

Câu trả lời:


388

Nếu bạn chỉ muốn chạy hàm được cung cấp useEffectsau kết xuất ban đầu, bạn có thể cung cấp cho nó một mảng trống làm đối số thứ hai.

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

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

27
Ngoài ra, nếu có các thông số bạn sử dụng để tìm nạp dữ liệu (ví dụ: id người dùng), bạn có thể chuyển id người dùng trong mảng đó và nếu nó thay đổi, thành phần sẽ lấy lại dữ liệu. Nhiều trường hợp sử dụng sẽ làm việc như vậy.
trixn

4
vâng ... thông tin thêm về việc bỏ qua được ghi lại ở đây: Reacjs.org/docs/iêu
Melounek

Đây có vẻ như là câu trả lời đơn giản nhất, nhưng ESLint phàn nàn ... hãy xem câu trả lời khác về chủ đề này stackoverflow.com/a/56767883/1550587
Simon Hutchison

85

TL; DR

useEffect(yourCallback, []) - sẽ kích hoạt cuộc gọi lại chỉ sau lần kết xuất đầu tiên.

Giải thích chi tiết

useEffectchạy theo mặc định sau mỗi lần kết xuất thành phần (do đó gây ra hiệu ứng).

Khi đặt useEffectvào thành phần của bạn, bạn nói với React rằng bạn muốn chạy gọi lại như một hiệu ứng. React sẽ chạy hiệu ứng sau khi kết xuất và sau khi thực hiện các cập nhật DOM.

Nếu bạn chỉ chuyển một cuộc gọi lại - cuộc gọi lại sẽ chạy sau mỗi lần kết xuất.

Nếu truyền đối số thứ hai (mảng), React sẽ chạy lại cuộc gọi sau lần kết xuất đầu tiên và mỗi khi một trong các phần tử trong mảng bị thay đổi. ví dụ như khi đặt useEffect(() => console.log('hello'), [someVar, someOtherVar])- gọi lại sẽ chạy sau khi người đầu tiên đưa ra và sau khi bất kỳ và việc này là một trong someVarhoặc someOtherVarđược thay đổi.

Bằng cách chuyển đối số thứ hai một mảng trống, React sẽ so sánh sau mỗi kết xuất mảng và sẽ thấy không có gì thay đổi, do đó chỉ gọi lại cuộc gọi sau lần kết xuất đầu tiên.


68

hookMountEffect

Chạy một chức năng chỉ một lần sau khi gắn kết thành phần là một mô hình phổ biến đến mức nó biện minh cho một cái móc của chính nó che giấu các chi tiết thực hiện.

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

Sử dụng nó trong bất kỳ thành phần chức năng.

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

Giới thiệu về hook useMountEffect

Khi sử dụng useEffectvới đối số mảng thứ hai, React sẽ chạy gọi lại sau khi gắn (kết xuất ban đầu) và sau khi các giá trị trong mảng đã thay đổi. Vì chúng ta vượt qua một mảng trống, nó sẽ chỉ chạy sau khi gắn.


19
Tôi rất thích câu trả lời của bạn, vì quy tắc "phản ứng móc / cạn kiệt" của ESLint sẽ luôn thất bại trong danh sách phụ thuộc trống. Và ví dụ, mẫu ứng dụng tạo phản ứng nổi tiếng sẽ thực thi quy tắc đó.
Dynalon

1
Hoàn toàn đồng ý với @Dynalon. Đây phải là giải pháp được chấp nhận vì nó không can thiệp vào quy tắc của
ESLint

Cảm ơn @Dynalon và Mikado68 :-). Quyết định là một đặc quyền của OP. Tôi đã được thông báo về ý kiến ​​của bạn, nhưng OP thì không. Bạn có thể gợi ý cho anh ấy bằng cách bình luận trực tiếp về câu hỏi.
Cá chép Ben

2
Bây giờ bạn có thể sử dụng useMountkhi chức năng hiệu ứng của bạn cần thứ gì đó từ đạo cụ nhưng không bao giờ cần chạy lại ngay cả khi giá trị đó thay đổi mà không useEffect(()=>console.log(props.val),[])có cảnh báo kẻ nói dối: sẽ thiếu cảnh báo phụ thuộc nhưng useMount(()=>console.log(props.val))sẽ không gây ra cảnh báo nhưng "không hoạt động". Tôi không chắc chắn nếu có vấn đề với chế độ đồng thời.
HMR

1
Tôi thích cái này :) Lý do tương tự như trạng thái trước đây; quy tắc ESLint sẽ không phàn nàn về điều này, cộng với tên của nó dễ hiểu hơn một mảng trống
Frexuz

20

Truyền một mảng trống làm đối số thứ hai useEffect. Điều này thực sự nói với React, trích dẫn các tài liệu :

Điều này cho React biết rằng hiệu ứng của bạn không phụ thuộc vào bất kỳ giá trị nào từ đạo cụ hoặc trạng thái, do đó, nó không bao giờ cần phải chạy lại.

Đây là một đoạn mà bạn có thể chạy để cho thấy rằng nó hoạt động:

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

Tôi muốn xác định một mountchức năng, nó đánh lừa EsLint theo cách tương tự useMountvà tôi thấy nó tự giải thích nhiều hơn.

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

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

0

Thủ thuật là useEffect lấy tham số thứ hai.

Tham số thứ hai là một mảng các biến mà thành phần sẽ kiểm tra để đảm bảo nó thay đổi trước khi kết xuất lại. Bạn có thể đặt bất kỳ bit nào của đạo cụ và trạng thái bạn muốn vào đây để kiểm tra.

Hoặc, không đặt gì:

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>
  );
}

Điều đó sẽ đảm bảo useEffect chỉ chạy một lần.

Lưu ý từ các tài liệu:

Nếu bạn sử dụng tối ưu hóa này, hãy đảm bảo mảng bao gồm tất cả các giá trị từ phạm vi thành phần (chẳng hạn như đạo cụ và trạng thái) thay đổi theo thời gian và được sử dụng bởi hiệu ứng. Nếu không, mã của bạn sẽ tham chiếu các giá trị cũ từ các kết xuất trước đó.


3
Nếu bạn sao chép / dán theo nghĩa đen từ Thủ thuật CSS, thì điều tối thiểu bạn có thể làm là ghi có nguồn của mình. css-tricks.com/run-useeffect-only-once
burtyish
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.