Làm cách nào để khắc phục cảnh báo phụ thuộc bị thiếu khi sử dụng hookEffect React Hook?


172

Với React 16.8.6 (phiên bản 16.8.3 trước đó rất tốt), tôi gặp lỗi này khi cố gắng ngăn chặn một vòng lặp vô hạn trong yêu cầu tìm nạp

./src/components/BusinessesList.js
Line 51:  React Hook useEffect has a missing dependency: 'fetchBusinesses'.
Either include it or remove the dependency array  react-hooks/exhaustive-deps

Tôi đã không thể tìm ra giải pháp ngăn chặn vòng lặp vô hạn. Tôi muốn tránh xa việc sử dụng useReducer(). Tôi đã tìm thấy cuộc thảo luận này https://github.com/facebook/react/issues/14920 trong đó một giải pháp khả thi là You can always // eslint-disable-next-line react-hooks/exhaustive-deps if you think you know what you're doing.tôi không tự tin vào những gì tôi đang làm nên tôi chưa thử thực hiện nó.

Tôi có thiết lập hiện tại này React hook useEffect chạy liên tục mãi mãi / vòng lặp vô hạn và nhận xét duy nhất là về useCallback()điều mà tôi không quen thuộc.

Cách tôi hiện đang sử dụng useEffect()(mà tôi chỉ muốn chạy một lần khi bắt đầu tương tự componentDidMount())

useEffect(() => {
    fetchBusinesses();
  }, []);
const fetchBusinesses = () => {
    return fetch("theURL", {method: "GET"}
    )
      .then(res => normalizeResponseErrors(res))
      .then(res => {
        return res.json();
      })
      .then(rcvdBusinesses => {
        // some stuff
      })
      .catch(err => {
        // some error handling
      });
  };

Câu trả lời:


187

Nếu bạn không sử dụng phương thức fetchBusinesses ở bất kỳ đâu ngoài hiệu ứng, bạn chỉ cần chuyển nó vào hiệu ứng và tránh cảnh báo

useEffect(() => {
    const fetchBusinesses = () => {
       return fetch("theURL", {method: "GET"}
    )
      .then(res => normalizeResponseErrors(res))
      .then(res => {
        return res.json();
      })
      .then(rcvdBusinesses => {
        // some stuff
      })
      .catch(err => {
        // some error handling
      });
  };
  fetchBusinesses();
}, []);

Tuy nhiên, nếu bạn đang sử dụng fetchBusinesses ngoài kết xuất, bạn phải lưu ý hai điều

  1. Có vấn đề gì với bạn không vượt qua fetchBusinessesnhư một phương thức khi nó được sử dụng trong quá trình gắn kết với việc đóng cửa kèm theo không?
  2. Phương thức của bạn có phụ thuộc vào một số biến mà nó nhận được từ bao đóng không? Đây không phải là trường hợp cho bạn.
  3. Trên mỗi kết xuất, fetchBusinesses sẽ được tạo lại và do đó việc chuyển nó sang useEffect sẽ gây ra sự cố. Vì vậy, trước tiên bạn phải ghi nhớ fetchBusinesses nếu bạn chuyển nó sang mảng phụ thuộc.

Tóm lại, tôi sẽ nói rằng nếu bạn đang sử dụng fetchBusinessesbên ngoài useEffectbạn có thể vô hiệu hóa quy tắc bằng cách // eslint-disable-next-line react-hooks/exhaustive-depskhác, bạn có thể di chuyển phương thức bên trong useEffect

Để vô hiệu hóa quy tắc, bạn sẽ viết nó như thế nào

useEffect(() => {
   // other code
   ...

   // eslint-disable-next-line react-hooks/exhaustive-deps
}, []) 

14
Tôi đã sử dụng giải pháp bạn vạch ra độc đáo. Một giải pháp khác mà tôi đã sử dụng khác vì một thiết lập khác là useCallback(). Vì vậy, ví dụ: const fetchBusinesses= useCallback(() => { ... }, [...])useEffect()sẽ trông như thế này:useEffect(() => { fetchBusinesses(); }, [fetchBusinesses]);
russ

1
@russ, bạn đã đúng, bạn sẽ cần ghi nhớ fetchBusiness bằng cách sử dụng useCallback nếu bạn chuyển nó sang mảng phụ thuộc
Shubham Khatri

Sẽ thật tuyệt nếu bạn chỉ ra nơi đặt câu lệnh vô hiệu hóa eslint. Tôi nghĩ rằng nó sẽ ở trên useEffect
user210757

1
sử dụng // eslint-disable-next-line react-hooks/exhaustive-depsđể giải thích cho người nói dối rằng mã của bạn đúng giống như một bản hack. Tôi hy vọng họ sẽ tìm ra giải pháp khác để làm cho kẻ nói dối đủ thông minh hơn để phát hiện khi một cuộc tranh luận không bắt buộc
Olivier Boissé

1
@TapasAdhikary, vâng, bạn có thể có chức năng async trong useEffect, bạn chỉ cần viết nó khác đi. Vui lòng kiểm tra stackoverflow.com/questions/53332321/
Mạnh

75
./src/components/BusinessesList.js
Line 51:  React Hook useEffect has a missing dependency: 'fetchBusinesses'.
Either include it or remove the dependency array  react-hooks/exhaustive-deps

Đó không phải là lỗi JS / React mà là cảnh báo eslint (eslint-plugin-Reac-hook).

Nó cho bạn biết rằng hook phụ thuộc vào chức năng fetchBusinesses, vì vậy bạn nên vượt qua nó như là phụ thuộc.

useEffect(() => {
  fetchBusinesses();
}, [fetchBusinesses]);

Nó có thể dẫn đến việc gọi hàm mỗi kết xuất nếu hàm được khai báo trong thành phần như:

const Component = () => {
  /*...*/

  //new function declaration every render
  const fetchBusinesses = () => {
    fetch('/api/businesses/')
      .then(...)
  }

  useEffect(() => {
    fetchBusinesses();
  }, [fetchBusinesses]);

  /*...*/
}

bởi vì mỗi hàm thời gian được khai báo lại với tham chiếu mới

Cách chính xác để làm công cụ này là:

const Component = () => {
  /*...*/

  // keep function reference
  const fetchBusinesses = useCallback(() => {
    fetch('/api/businesses/')
      .then(...)
  }, [/* additional dependencies */]) 

  useEffect(() => {
    fetchBusinesses();
  }, [fetchBusinesses]);

  /*...*/
}

hoặc chỉ xác định chức năng trong useEffect

Thêm: https://github.com/facebook/react/issues/14920


13
Điều này dẫn đến một lỗi mớiLine 20: The 'fetchBusinesses' function makes the dependencies of useEffect Hook (at line 51) change on every render. Move it inside the useEffect callback. Alternatively, wrap the 'fetchBusinesses' definition into its own useCallback() Hook
russ

1
giải pháp là ổn và nếu trên chức năng bạn sửa đổi một trạng thái khác, bạn phải thêm các phụ thuộc để tránh một hành vi bất ngờ khác
ông Caesarlarsson

53

Bạn có thể đặt nó trực tiếp làm cuộc useEffectgọi lại:

useEffect(fetchBusinesses, [])

Nó sẽ chỉ kích hoạt một lần, vì vậy hãy đảm bảo tất cả các phụ thuộc của hàm được đặt chính xác (giống như sử dụng componentDidMount/componentWillMount...)


Chỉnh sửa 21/2/2020

Chỉ để hoàn thiện:

1. Sử dụng chức năng useEffectgọi lại (như trên)

useEffect(fetchBusinesses, [])

2. Khai báo hàm bên trong useEffect()

useEffect(() => {
  function fetchBusinesses() {
    ...
  }
  fetchBusinesses()
}, [])

3. Ghi nhớ với useCallback()

Trong trường hợp này, nếu bạn có các phụ thuộc trong hàm, bạn sẽ phải đưa chúng vào useCallbackmảng phụ thuộc và điều này sẽ kích hoạt useEffectlại nếu các thông số của hàm thay đổi. Bên cạnh đó, nó là rất nhiều nồi hơi ... Vì vậy, chỉ cần chuyển trực tiếp chức năng useEffectnhư trong 1. useEffect(fetchBusinesses, []).

const fetchBusinesses = useCallback(() => {
  ...
}, [])
useEffect(() => {
  fetchBusinesses()
}, [fetchBusinesses])

4. Vô hiệu hóa cảnh báo của eslint

useEffect(() => {
  fetchBusinesses()
}, []) // eslint-disable-line react-hooks/exhaustive-deps

2
Tôi yêu bạn ... Câu trả lời này rất đầy đủ!
Nick09

8

Giải pháp cũng được đưa ra bằng phản ứng, họ khuyên bạn nên sử dụng useCallbacksẽ trả về phiên bản ghi nhớ chức năng của bạn:

Hàm 'fetchBusinesses' làm cho các phụ thuộc của useEffect Hook (tại dòng NN) thay đổi trên mỗi kết xuất. Để khắc phục điều này, hãy bọc định nghĩa 'fetchBusinesses' vào useCallback () Hook hook-hook / Wasteive-deps của chính nó

useCallbackrất đơn giản để sử dụng vì nó có cùng chữ ký với useEffectsự khác biệt là useCallback trả về một hàm. Nó sẽ trông như thế này:

 const fetchBusinesses = useCallback( () => {
        return fetch("theURL", {method: "GET"}
    )
    .then(() => { /* some stuff */ })
    .catch(() => { /* some error handling */ })
  }, [/* deps */])
  // We have a first effect thant uses fetchBusinesses
  useEffect(() => {
    // do things and then fetchBusinesses
    fetchBusinesses(); 
  }, [fetchBusinesses]);
   // We can have many effect thant uses fetchBusinesses
  useEffect(() => {
    // do other things and then fetchBusinesses
    fetchBusinesses();
  }, [fetchBusinesses]);


1

Bài viết này là một mồi tốt về tìm nạp dữ liệu bằng các hook: https://www.robinwieruch.de/react-hooks-fetch-data/

Về cơ bản, bao gồm định nghĩa hàm tìm nạp bên trong useEffect:

useEffect(() => {
  const fetchBusinesses = () => {
    return fetch("theUrl"...
      // ...your fetch implementation
    );
  }

  fetchBusinesses();
}, []);

1

Bạn có thể loại bỏ mảng loại đối số thứ 2 []nhưng fetchBusinesses()cũng sẽ được gọi mỗi bản cập nhật. Bạn có thể thêm một IFtuyên bố vào việc fetchBusinesses()thực hiện nếu bạn muốn.

React.useEffect(() => {
  fetchBusinesses();
});

Một cái khác là để thực hiện fetchBusinesses()chức năng bên ngoài thành phần của bạn. Chỉ cần đừng quên chuyển bất kỳ đối số phụ thuộc vào fetchBusinesses(dependency)cuộc gọi của bạn , nếu có.

function fetchBusinesses (fetch) {
  return fetch("theURL", { method: "GET" })
    .then(res => normalizeResponseErrors(res))
    .then(res => res.json())
    .then(rcvdBusinesses => {
      // some stuff
    })
    .catch(err => {
      // some error handling
    });
}

function YourComponent (props) {
  const { fetch } = props;

  React.useEffect(() => {
    fetchBusinesses(fetch);
  }, [fetch]);

  // ...
}

0

Trên thực tế, các cảnh báo rất hữu ích khi bạn phát triển với móc. nhưng trong một số trường hợp, nó có thể kim bạn. đặc biệt là khi bạn không cần lắng nghe sự thay đổi phụ thuộc.

Nếu bạn không muốn đặt fetchBusinessesbên trong các phụ thuộc của hook, bạn chỉ cần chuyển nó làm đối số cho hàm gọi lại của hook và đặt fetchBusinessesgiá trị chính làm giá trị mặc định cho nó như thế này

useEffect((fetchBusinesses = fetchBusinesses) => {
   fetchBusinesses();
}, []);

Nó không phải là thực hành tốt nhất nhưng nó có thể hữu ích trong một số trường hợp.

Cũng như Shubnam đã viết, bạn có thể thêm mã bên dưới để thông báo cho ESLint bỏ qua việc kiểm tra hook của bạn.

// eslint-disable-next-line react-hooks/exhaustive-deps

0

Tôi chỉ muốn chạy [ fetchBusinesses] một lần khi bắt đầu tương tự nhưcomponentDidMount()

Bạn có thể kéo fetchBusinesseshoàn toàn ra khỏi thành phần của bạn:

const fetchBusinesses = () => { // or pass some additional input from component as args
  return fetch("theURL", { method: "GET" }).then(n => process(n));
};

const Comp = () => {
  React.useEffect(() => {
    fetchBusinesses().then(someVal => {
      // ... do something with someVal
    });
  }, []); // eslint warning solved!
  return <div>{state}</div>;
};

Điều này sẽ không chỉ cung cấp một giải pháp đơn giản và giải quyết cảnh báo toàn diện. fetchBusinessbây giờ có thể được kiểm tra tốt hơn và giảm bớt Comp, vì nó nằm trong phạm vi mô-đun bên ngoài cây React.

Di dời fetchBusinessesbên ngoài hoạt động tốt ở đây, vì chúng ta chỉ có thể đọc các đạo cụ và trạng thái ban đầu từ thành phần dù sao do phạm vi đóng cửa cũ ( []dep in useEffect).

Làm thế nào để bỏ qua các phụ thuộc chức năng

  • Di chuyển chức năng bên trong hiệu ứng
  • Di chuyển chức năng bên ngoài thành phần - (chúng tôi đang sử dụng chức năng này)
  • Hàm gọi trong khi kết xuất và cho phép useEffectphụ thuộc vào giá trị này (hàm tính toán thuần túy)
  • Thêm chức năng để tạo hiệu ứng deps và bọc nó useCallbacknhư là phương sách cuối cùng

Liên quan đến các giải pháp khác:

Kéo fetchBusinessesvào bên trong useEffect()không thực sự có ích, nếu bạn truy cập vào trạng thái khác trong đó. eslint vẫn sẽ phàn nàn: Codandbox .

Tôi cũng sẽ giữ lại từ eslint đầy đủ-deps bỏ qua ý kiến. Thật dễ dàng để quên chúng khi bạn thực hiện tái cấu trúc và đại tu các phụ thuộc của mình.


0
const [mount, setMount] = useState(false)
const fetchBusinesses = () => { 
   //function defination
}
useEffect(() => {
   if(!mount) {
      setMount(true);
      fetchBusinesses();
   }
},[fetchBusinesses]);

Đây là giải pháp khá đơn giản và bạn không cần phải ghi đè lên các cảnh báo es-lint. Chỉ cần duy trì một cờ để kiểm tra xem thành phần được gắn kết hay không.


0

bạn thử cách này

const fetchBusinesses = () => {
    return fetch("theURL", {method: "GET"}
    )
      .then(res => normalizeResponseErrors(res))
      .then(res => {
        return res.json();
      })
      .then(rcvdBusinesses => {
        // some stuff
      })
      .catch(err => {
        // some error handling
      });
  };

useEffect(() => {
    fetchBusinesses();
  });

nó làm việc cho bạn Nhưng đề nghị của tôi là thử cách này cũng làm việc cho bạn. Nó tốt hơn so với trước đây. Tôi sử dụng cách này:

useEffect(() => {
        const fetchBusinesses = () => {
    return fetch("theURL", {method: "GET"}
    )
      .then(res => normalizeResponseErrors(res))
      .then(res => {
        return res.json();
      })
      .then(rcvdBusinesses => {
        // some stuff
      })
      .catch(err => {
        // some error handling
      });
  };
        fetchBusinesses();
      }, []);

nếu bạn nhận được dữ liệu trên cơ sở của id cụ thể thì hãy thêm vào gọi lại useEffect [id]sau đó không thể hiển thị cảnh báo cho bạn React Hook useEffect has a missing dependency: 'any thing'. Either include it or remove the dependency array


-4

chỉ cần vô hiệu hóa eslint cho dòng tiếp theo;

useEffect(() => {
   fetchBusinesses();
// eslint-disable-next-line
}, []);

theo cách này, bạn đang sử dụng nó giống như một thành phần đã gắn kết (được gọi một lần)

cập nhật

hoặc là

const fetchBusinesses = useCallback(() => {
 // your logic in here
 }, [someDeps])

useEffect(() => {
   fetchBusinesses();
// no need to skip eslint warning
}, [fetchBusinesses]); 

fetchBusinesses sẽ được gọi mỗi khi một sốDeps sẽ thay đổi


thay vì vô hiệu hóa, chỉ cần làm điều này: [fetchBusinesses]sẽ tự động xóa cảnh báo và điều đó đã giải quyết vấn đề cho tôi.
rotimi-tốt nhất

7
@RotimiBest - làm điều này gây ra kết xuất lại vô hạn như được mô tả trong câu hỏi
user210757

Tôi thực sự đã làm theo cách này trong một trong những dự án của tôi trước đây và nó không tạo ra một vòng lặp vô hạn. Tôi sẽ kiểm tra lại mặc dù.
rotimi-best

@ user210757 Đợi nhưng tại sao nó sẽ gây ra một vòng lặp vô hạn, không giống như bạn đang đặt trạng thái sau khi tìm nạp dữ liệu từ máy chủ. Nếu bạn đang cập nhật trạng thái, chỉ cần viết một điều kiện if trước khi gọi hàm trong useEffectđó kiểm tra xem trạng thái có trống không.
rotimi-tốt nhất

@ rotimi-best là ahwile kể từ khi tôi nhận xét nhưng tôi sẽ nói rằng chức năng này được tạo lại mỗi lần vì vậy sẽ không bao giờ giống nhau, vì vậy bạn sẽ luôn kết xuất lại, trừ khi bạn di chuyển vào cơ thể useEffect hoặc useCallback
user210757
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.