Bagaimana cara memperbaiki peringatan ketergantungan yang hilang saat menggunakan useEffect React Hook?

178

Dengan Bereaksi 16.8.6 (itu bagus pada versi sebelumnya 16.8.3), saya mendapatkan kesalahan ini ketika saya mencoba untuk mencegah loop tak terbatas pada permintaan pengambilan

./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

Saya tidak dapat menemukan solusi yang menghentikan infinite loop. Saya ingin tinggal jauh dari menggunakan useReducer(). Saya memang menemukan diskusi ini https://github.com/facebook/react/issues/14920 di mana solusi yang memungkinkan adalah You can always // eslint-disable-next-line react-hooks/exhaustive-deps if you think you know what you're doing.saya tidak yakin dengan apa yang saya lakukan jadi saya belum mencoba mengimplementasikannya dulu.

Saya memiliki pengaturan saat ini React hook useEffect berjalan terus menerus selamanya / infinite loop dan satu-satunya komentar adalah tentang useCallback()yang saya tidak kenal.

Bagaimana saya saat ini menggunakan useEffect()(yang saya hanya ingin menjalankan sekali di awal mirip dengan 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
      });
  };
russ
sumber

Jawaban:

191

Jika Anda tidak menggunakan metode fetchBusinesses di mana pun selain dari efeknya, Anda bisa memindahkannya ke efek dan menghindari peringatan

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();
}, []);

Namun jika Anda menggunakan fetchBusinesses di luar render, Anda harus mencatat dua hal

  1. Apakah ada masalah dengan Anda tidak lulus fetchBusinessessebagai metode ketika digunakan selama mount dengan penutupan terlampir?
  2. Apakah metode Anda bergantung pada beberapa variabel yang diterima dari penutupan terlampir? Ini tidak berlaku untuk Anda.
  3. Pada setiap render, fetchBusinesses akan dibuat kembali dan karenanya meneruskannya ke useEffect akan menyebabkan masalah. Jadi pertama-tama Anda harus memoize fetchBusinesses jika Anda akan meneruskannya ke array dependensi.

Singkatnya, saya akan mengatakan bahwa jika Anda menggunakan di fetchBusinessesluar useEffectAnda dapat menonaktifkan aturan menggunakan // eslint-disable-next-line react-hooks/exhaustive-depsjika tidak, Anda dapat memindahkan metode di dalam useEffect

Untuk menonaktifkan aturan Anda akan suka

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

   // eslint-disable-next-line react-hooks/exhaustive-deps
}, []) 
Shubham Khatri
sumber
14
Saya menggunakan solusi yang Anda uraikan dengan baik. Solusi lain yang saya gunakan di tempat lain karena setup yang berbeda useCallback(). Jadi misalnya: const fetchBusinesses= useCallback(() => { ... }, [...]) dan useEffect()akan terlihat seperti ini:useEffect(() => { fetchBusinesses(); }, [fetchBusinesses]);
russ
1
@russ, Anda benar, Anda perlu memo fetchBusiness menggunakan useCallback jika Anda ingin meneruskannya ke array dependensi
Shubham Khatri
Akan lebih baik jika Anda menunjukkan di mana harus meletakkan pernyataan eslint-disable. Saya pikir itu akan di atas useEffect
user210757
1
gunakan // eslint-disable-next-line react-hooks/exhaustive-depsuntuk menjelaskan kepada linter bahwa kode Anda benar seperti hack. Saya berharap mereka akan menemukan solusi lain untuk membuat linter lebih pintar untuk dideteksi ketika sebuah argumen tidak wajib
Olivier Boissé
1
@ TapasAdhikary, ya Anda dapat memiliki fungsi async di useEffect, Anda hanya perlu menuliskannya secara berbeda. Silakan periksa stackoverflow.com/questions/53332321/…
Shubham Khatri
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

Ini bukan JS / React error tetapi peringatan eslint (eslint-plugin-react-hooks).

Ini memberi tahu Anda bahwa kait tergantung pada fungsinya fetchBusinesses, jadi Anda harus meneruskannya sebagai ketergantungan.

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

Itu bisa mengakibatkan memanggil fungsi setiap render jika fungsi dideklarasikan dalam komponen seperti:

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

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

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

  /*...*/
}

karena setiap fungsi waktu dideklar ulang dengan referensi baru

Cara yang benar dalam melakukan hal ini adalah:

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

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

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

  /*...*/
}

atau hanya mendefinisikan fungsi dalam useEffect

Lebih lanjut: https://github.com/facebook/react/issues/14920

fard
sumber
14
Ini menghasilkan kesalahan baruLine 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
solusinya baik-baik saja dan jika pada fungsi Anda memodifikasi keadaan lain Anda harus menambahkan dependecies untuk menghindari perilaku tak terduga lainnya
cesarlarsson
57

Anda dapat mengaturnya langsung sebagai useEffectpanggilan balik:

useEffect(fetchBusinesses, [])

Ini akan memicu hanya sekali, jadi pastikan semua dependensi fungsi diatur dengan benar (sama seperti menggunakan componentDidMount/componentWillMount...)


Edit 02/21/2020

Hanya untuk kelengkapan:

1. Gunakan fungsi sebagai useEffectpanggilan balik (seperti di atas)

useEffect(fetchBusinesses, [])

2. Nyatakan fungsi di dalamnya useEffect()

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

3. Memoize dengan useCallback()

Dalam hal ini, jika Anda memiliki dependensi dalam fungsi Anda, Anda harus memasukkannya dalam useCallbackarray dependensi dan ini akan memicu useEffectlagi jika params fungsi berubah. Selain itu, ini banyak boilerplate ... Jadi langsung saja operasikan fungsinya useEffectseperti pada 1. useEffect(fetchBusinesses, []).

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

4. Nonaktifkan peringatan eslint

useEffect(() => {
  fetchBusinesses()
}, []) // eslint-disable-line react-hooks/exhaustive-deps
jpenna
sumber
2
Aku mencintaimu ... Jawaban ini sangat lengkap!
Nick09
8

Solusi ini juga diberikan oleh react, saran yang Anda gunakan useCallbackyang akan mengembalikan versi memoize dari fungsi Anda:

Fungsi 'fetchBusinesses' membuat dependensi useEffect Hook (pada baris NN) berubah pada setiap render. Untuk memperbaikinya, bungkus definisi 'fetchBusinesses' ke dalam useCallback sendiri () Hook-hooks / exhaust-deps

useCallbackmudah digunakan karena memiliki tanda tangan yang sama karena useEffectperbedaannya adalah useCallback mengembalikan fungsi. Akan terlihat seperti ini:

 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]);
Stephane L
sumber
1

Artikel ini adalah primer yang bagus untuk mengambil data dengan kait: https://www.robinwieruch.de/react-hooks-fetch-data/

Pada dasarnya, sertakan definisi fungsi pengambilan di dalam useEffect:

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

  fetchBusinesses();
}, []);
helloitsjoe
sumber
1

Anda dapat menghapus larik tipe argumen 2 []tetapi fetchBusinesses()akan juga dipanggil setiap pembaruan. Anda dapat menambahkan IFpernyataan ke dalam fetchBusinesses()implementasi jika Anda mau.

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

Yang lainnya adalah mengimplementasikan fetchBusinesses()fungsi di luar komponen Anda. Hanya saja, jangan lupa memberikan argumen ketergantungan pada fetchBusinesses(dependency)panggilan Anda , jika ada.

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

  // ...
}
5ervant
sumber
0

Sebenarnya peringatan itu sangat berguna ketika Anda berkembang dengan kait. tetapi dalam beberapa kasus, itu bisa menusuk Anda. terutama ketika Anda tidak perlu mendengarkan perubahan dependensi.

Jika Anda tidak ingin memasukkan fetchBusinessesdependensi hook, Anda bisa meneruskannya sebagai argumen ke callback hook dan mengatur main fetchBusinessessebagai nilai default untuk itu seperti ini

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

Ini bukan praktik terbaik tetapi bisa bermanfaat dalam beberapa kasus.

Juga seperti yang ditulis Shubnam, Anda dapat menambahkan kode di bawah ini untuk memberi tahu ESLint untuk mengabaikan pemeriksaan untuk kail Anda.

// eslint-disable-next-line react-hooks/exhaustive-deps
Behnam Azimi
sumber
0

Saya hanya ingin menjalankan [ fetchBusinesses] sekali di awal yang mirip dengancomponentDidMount()

Anda dapat menarik fetchBusinessessepenuhnya dari komponen Anda:

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

Ini tidak hanya akan memberikan solusi sederhana dan menyelesaikan peringatan deps-lengkap. fetchBusinesssekarang dapat diuji lebih baik dan lebih mudah Comp, karena berada dalam lingkup modul di luar pohon Bereaksi.

Pindah ke fetchBusinessesluar berfungsi dengan baik di sini, karena kami hanya akan dapat membaca alat peraga awal dan menyatakan dari komponen karena ruang lingkup penutupan basi ( []dep in useEffect).

Bagaimana menghilangkan dependensi fungsi

  • Pindahkan fungsi di dalam efek
  • Pindahkan fungsi di luar komponen - (kami menggunakan ini)
  • Fungsi panggilan selama rendering dan biarkan useEffecttergantung pada nilai ini (fungsi komputasi murni)
  • Tambahkan fungsi untuk efek deps dan bungkus dengan useCallbacksebagai upaya terakhir

Mengenai solusi lain:

Menarik ke fetchBusinessesdalam useEffect()tidak terlalu membantu, jika Anda mengakses keadaan lain di dalamnya. eslint masih akan mengeluh: Codesandbox .

Saya juga akan menahan diri dari eslint-deps mengabaikan komentar. Hanya untuk mudah melupakan mereka ketika Anda melakukan refactoring dan perbaikan dependensi Anda.

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

Solusi ini cukup sederhana dan Anda tidak perlu mengesampingkan peringatan es-lint. Cukup pertahankan bendera untuk memeriksa apakah komponen sudah terpasang atau tidak.

Yasin
sumber
0

kamu coba cara ini

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

dan

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

ini bekerja untukmu. Tapi saran saya coba cara ini juga bekerja untuk Anda. Lebih baik dari sebelumnya. Saya menggunakan cara ini:

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();
      }, []);

jika Anda mendapatkan data berdasarkan id tertentu kemudian tambahkan callback useEffect [id]maka Anda tidak dapat menampilkan peringatan React Hook useEffect has a missing dependency: 'any thing'. Either include it or remove the dependency array

Kashif
sumber
-4

cukup nonaktifkan eslint untuk baris berikutnya;

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

dengan cara ini Anda menggunakannya seperti komponen melakukan mount (dipanggil sekali)

diperbarui

atau

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

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

fetchBusinesses akan dipanggil setiap kali beberapaDEP akan berubah

pengguna3550446
sumber
alih-alih menonaktifkan, hanya melakukan ini: [fetchBusinesses]akan secara otomatis menghapus peringatan dan itu memecahkan masalah bagi saya.
rotimi-best
7
@RotimiBest - melakukan ini menyebabkan render ulang tanpa batas seperti yang dijelaskan dalam pertanyaan
user210757
Saya benar-benar melakukannya dengan cara ini di salah satu proyek saya beberapa waktu yang lalu dan tidak menghasilkan loop yang tak terbatas. Saya akan periksa lagi.
rotimi-best
@ user210757 Tunggu tapi mengapa itu menyebabkan loop tak terbatas, ini tidak seperti Anda mengatur negara setelah mengambil data dari server. Jika Anda memperbarui keadaan, tulis saja kondisi if sebelum memanggil fungsi di useEffectyang memeriksa apakah keadaan kosong.
rotimi-best
@ rotimi-terbaik ahwile sejak saya berkomentar tapi saya akan mengatakan bahwa fungsi ini dibuat kembali setiap waktu sehingga tidak pernah sama sehingga akan selalu di-render kembali, kecuali jika Anda pindah ke tubuh useEffect atau useCallback
user210757