React: [ESLint] Umpan balik untuk aturan lint 'exhaustive-deps'

Dibuat pada 21 Feb 2019  Β·  111Komentar  Β·  Sumber: facebook/react

Jawaban Umum

πŸ’‘πŸ’‘πŸ’‘πŸ’‘πŸ’‘πŸ’‘πŸ’‘πŸ’‘πŸ’‘πŸ’‘πŸ’‘πŸ’‘πŸ’‘πŸ’‘πŸ’‘πŸ’‘πŸ’‘πŸ’‘πŸ’‘πŸ’‘πŸ’‘πŸ’‘

Kami menganalisis komentar pada posting ini untuk memberikan beberapa panduan: https://github.com/facebook/react/issues/14920#issuecomment -471070149.

πŸ’‘πŸ’‘πŸ’‘πŸ’‘πŸ’‘πŸ’‘πŸ’‘πŸ’‘πŸ’‘πŸ’‘πŸ’‘πŸ’‘πŸ’‘πŸ’‘πŸ’‘πŸ’‘πŸ’‘πŸ’‘πŸ’‘πŸ’‘πŸ’‘πŸ’‘


Apa ini

Ini adalah aturan ESLint baru yang memverifikasi daftar dependensi untuk Hooks seperti useEffect dan sejenisnya, melindungi dari perangkap penutupan basi. Untuk sebagian besar kasus, ia memiliki perbaikan otomatis. Kami akan menambahkan lebih banyak dokumentasi selama beberapa minggu ke depan.

autofix demo

Instalasi

yarn add eslint-plugin-react-hooks<strong i="18">@next</strong>
# or
npm install eslint-plugin-react-hooks<strong i="19">@next</strong>

Konfigurasi ESLint:

{
  "plugins": ["react-hooks"],
  // ...
  "rules": {
    "react-hooks/rules-of-hooks": 'error',
    "react-hooks/exhaustive-deps": 'warn' // <--- THIS IS THE NEW RULE
  }
}

Kasus uji sederhana untuk memverifikasi aturan berfungsi:

function Foo(props) {
  useEffect(() => {
    console.log(props.name);
  }, []); // <-- should error and offer autofix to [props.name]
}

Aturan serat mengeluh tetapi kode saya baik-baik saja!

Jika aturan lint react-hooks/exhaustive-deps dijalankan untuk Anda tetapi menurut Anda kode Anda sudah benar , harap posting di edisi ini.


SEBELUM POSTING KOMENTAR

Harap sertakan tiga hal ini:

  1. CodeSandbox yang menunjukkan contoh kode minimal yang masih mengekspresikan maksud Anda (bukan "bilah foo" tetapi pola UI aktual yang Anda terapkan).
  2. Penjelasan tentang langkah -
  3. Penjelasan tentang API yang dimaksud dari Hook/komponen Anda.

please

Tapi kasus saya sederhana, saya tidak ingin memasukkan hal-hal itu!

Mungkin sederhana bagi Anda β€” tetapi sama sekali tidak sederhana bagi kami. Jika komentar Anda tidak menyertakan salah satu dari mereka (misalnya tidak ada tautan CodeSandbox), kami akan menyembunyikan komentar Anda karena sebaliknya sangat sulit untuk melacak diskusi. Terima kasih telah menghargai waktu semua orang dengan menyertakan mereka.

Tujuan akhir dari utas ini adalah untuk menemukan skenario umum dan mengubahnya menjadi dokumen dan peringatan yang lebih baik. Ini hanya dapat terjadi jika detail yang cukup tersedia. Komentar drive-by dengan cuplikan kode yang tidak lengkap secara signifikan menurunkan kualitas diskusi β€” sampai-sampai tidak sepadan.

ESLint Rules Discussion

Komentar yang paling membantu

Kami melewati ini dengan @threepointone hari ini. Berikut ringkasannya:

Diperbaiki dalam Aturan Lint

Ketergantungan useEffect asing

Aturan tidak mencegah Anda menambahkan deps "asing" ke useEffect lagi karena ada skenario yang sah.

Fungsi dalam komponen yang sama tetapi didefinisikan di luar efek

Linter tidak memperingatkan untuk kasus yang sekarang aman, tetapi dalam semua kasus lain ia memberi Anda saran yang lebih baik (seperti memindahkan fungsi di dalam efek, atau membungkusnya dengan useCallback ).

Layak Diperbaiki dalam Kode Pengguna

Menyetel ulang status pada perubahan alat peraga

Ini tidak menghasilkan pelanggaran serat lagi tetapi cara idiomatis untuk mengatur ulang status sebagai respons terhadap alat peraga berbeda . Solusi ini akan memiliki render ekstra tidak konsisten jadi saya tidak yakin itu diinginkan.

"Nilai non-fungsi saya konstan"

Kait mendorong Anda ke arah kebenaran sedapat mungkin. Jika Anda menentukan deps (yang dalam beberapa kasus Anda dapat menghilangkan), kami sangat menyarankan untuk menyertakan bahkan orang-orang yang Anda berpikir tidak akan berubah. Ya, dalam contoh useDebounce , penundaan tidak mungkin berubah. Tapi itu masih bug jika ya, tapi Hook tidak bisa mengatasinya. Ini juga muncul dalam skenario lain. (Misalnya Hooks jauh lebih kompatibel dengan hot reload karena setiap nilai diperlakukan sebagai dinamis.)

Jika Anda benar-benar bersikeras bahwa nilai tertentu statis, Anda dapat menerapkannya.
Cara teraman adalah melakukannya secara eksplisit di API Anda:

const useFetch = createFetch({ /* config object */});
const useDebounce = createDebounce(500);
const FormInput = createInput({ rules: [emailValidator, phoneValidator] });

Maka itu jelas tidak dapat berubah kecuali Anda memasukkannya ke dalam render. (Yang tidak akan menjadi penggunaan idiomatik dari Hook Anda.) Tetapi mengatakan bahwa <Slider min={50} /> tidak akan pernah dapat berubah tidak benar-benar valid β€” seseorang dapat dengan mudah mengubahnya menjadi <Slider min={state ? 50 : 100} /> . Bahkan, seseorang dapat melakukan ini:

let slider
if (isCelsius) {
  slider = <Slider min={0} max={100} />
} else {
  slider = <Slider min={32} max={212} />
}

Jika seseorang mengubah status isCelsius , komponen dengan asumsi min tidak pernah berubah akan gagal diperbarui. Tidak jelas dalam kasus ini bahwa Slider akan sama (tetapi itu karena memiliki posisi yang sama di pohon). Jadi ini adalah pijakan utama dalam hal membuat perubahan pada kode. Poin utama dari React adalah bahwa pembaruan dirender seperti status awal (Anda biasanya tidak dapat membedakan mana yang mana). Baik Anda merender nilai prop B, atau beralih dari nilai prop A ke B β€” itu akan terlihat dan berperilaku sama.

Meskipun ini tidak disarankan, dalam beberapa kasus, mekanisme penegakan bisa berupa Hook yang memperingatkan ketika nilai berubah (tetapi menyediakan yang pertama). Setidaknya itu lebih mungkin untuk diperhatikan.

function useMyHook(a) {
  const initialA = usePossiblyStaleValue(a);
  // ...
}

function usePossiblyStaleValue(value) {
  const ref = useRef(value);

  if (process.env.NODE_ENV !== 'production') {
    if (ref.current !== value) { // Or your custom comparison logic
      console.error(
        'Unlike normally in React, it is not supported ' +
        'to pass dynamic values to useMyHook(). Sorry!'
      );
    }
  }

  return ref.current;
}

Mungkin juga ada kasus yang sah di mana Anda tidak dapat menangani pembaruan. Seperti jika API tingkat bawah tidak mendukungnya, seperti plugin jQuery atau API DOM. Dalam hal ini peringatan masih diperlukan agar konsumen komponen Anda memahaminya. Atau, Anda dapat membuat komponen pembungkus yang menyetel ulang key pada pembaruan yang tidak kompatibel β€” memaksa remount bersih dengan props baru. Itu mungkin lebih baik untuk komponen daun seperti bilah geser atau kotak centang.

"Nilai fungsi saya konstan"

Pertama-tama, jika konstan dan diangkat ke lingkup tingkat atas maka linter tidak akan mengeluh. Tapi itu tidak membantu dengan hal-hal yang datang dari alat peraga atau konteks.

Jika itu benar-benar konstan maka menentukan dalam deps tidak sakit. Seperti kasus di mana fungsi setState di dalam Hook kustom dikembalikan ke komponen Anda, dan kemudian Anda memanggilnya dari sebuah efek. Aturan lint tidak cukup pintar untuk memahami tipuan seperti ini. Tetapi di sisi lain, siapa pun dapat membungkus panggilan balik itu nanti sebelum kembali, dan mungkin mereferensikan prop atau status lain di dalamnya. Maka itu tidak akan konstan! Dan jika Anda gagal menangani perubahan itu, Anda akan memiliki bug prop/state basi yang buruk. Jadi menentukannya adalah default yang lebih baik.

Namun, itu adalah kesalahpahaman bahwa nilai fungsi selalu konstan. Mereka lebih sering konstan di kelas karena pengikatan metode, meskipun itu menciptakan rentang bugnya sendiri . Tetapi secara umum, fungsi apa pun yang menutup nilai dalam komponen fungsi tidak dapat dianggap konstan. Aturan lint sekarang lebih cerdas dalam memberi tahu Anda apa yang harus dilakukan. (Seperti memindahkannya ke dalam efek β€” perbaikan paling sederhana β€” atau membungkusnya dengan useCallback .)

Ada masalah pada spektrum yang berlawanan dari ini, di mana Anda mendapatkan loop tak terbatas (nilai fungsi selalu berubah). Kami menangkapnya dalam aturan lint sekarang jika memungkinkan (dalam komponen yang sama) dan menyarankan perbaikan. Tapi itu rumit jika Anda melewati sesuatu beberapa tingkat ke bawah.

Anda masih dapat membungkusnya dengan useCallback untuk memperbaiki masalah. Ingat bahwa secara teknis itu valid untuk suatu fungsi untuk berubah , dan Anda tidak dapat mengabaikan kasus ini tanpa mempertaruhkan bug. Seperti onChange={shouldHandle ? handleChange : null} atau merender foo ? <Page fetch={fetchComments /> : <Page fetch={fetchArticles /> di tempat yang sama. Atau bahkan fetchComments yang menutup status komponen induk. Itu bisa berubah. Dengan kelas, perilakunya akan berubah secara diam-diam tetapi referensi fungsi akan tetap sama. Jadi anak Anda akan melewatkan pembaruan itu β€” Anda tidak benar-benar memiliki pilihan selain memberikan lebih banyak data kepada anak tersebut. Dengan komponen fungsi dan useCallback , identitas fungsi itu sendiri berubah β€” tetapi hanya jika diperlukan. Jadi itu adalah properti yang berguna dan bukan hanya penghalang yang harus dihindari.

Kita harus menambahkan solusi yang lebih baik untuk mendeteksi loop async tak terbatas. Itu harus mengurangi aspek yang paling membingungkan. Kami mungkin menambahkan beberapa deteksi untuk ini di masa mendatang. Anda juga dapat menulis sendiri sesuatu seperti ini:

useWarnAboutTooFrequentChanges([deps]);

Ini tidak ideal dan kita harus berpikir tentang menangani ini dengan lebih anggun. Saya setuju kasus seperti ini cukup buruk. Perbaikan tanpa melanggar aturan adalah membuat rules statis, misalnya dengan mengubah API menjadi createTextInput(rules) , dan membungkus register dan unregister menjadi useCallback . Lebih baik lagi, hapus register dan unregister , dan ganti dengan konteks terpisah di mana Anda meletakkan dispatch saja. Maka Anda dapat menjamin Anda tidak akan pernah memiliki identitas fungsi yang berbeda dari membacanya.

Saya akan menambahkan bahwa Anda mungkin ingin meletakkan useMemo pada nilai konteks karena penyedia melakukan banyak perhitungan yang akan menyedihkan untuk diulang jika tidak ada komponen baru yang terdaftar tetapi induknya sendiri diperbarui. Jadi ini menempatkan masalah yang mungkin tidak Anda sadari jika tidak lebih terlihat. Meskipun saya setuju kita perlu membuatnya lebih menonjol ketika ini terjadi.

Mengabaikan dependensi fungsi sepenuhnya mengarah ke bug yang lebih buruk dengan komponen fungsi dan Kait karena mereka akan terus melihat properti dan status basi jika Anda melakukannya. Jadi cobalah untuk tidak, ketika Anda bisa.

Bereaksi terhadap Perubahan Nilai Majemuk

Aneh bagi saya mengapa contoh ini menggunakan efek untuk sesuatu yang pada dasarnya adalah event handler. Melakukan "log" yang sama (saya berasumsi itu bisa berupa pengiriman formulir) di event handler tampaknya lebih pas. Ini terutama benar jika kita memikirkan apa yang terjadi ketika komponen dilepas. Bagaimana jika dilepas tepat setelah efek dijadwalkan? Hal-hal seperti pengiriman formulir seharusnya tidak hanya "tidak terjadi" dalam kasus itu. Jadi sepertinya efek mungkin pilihan yang salah di sana.

Yang mengatakan Anda masih dapat melakukan apa yang Anda coba β€” dengan membuat fullName menjadi setSubmittedData({firstName, lastName}) sebagai gantinya, dan kemudian [submittedData] adalah ketergantungan Anda, dari mana Anda dapat membaca firstName dan lastName .

Mengintegrasikan dengan Imperatif/Kode Warisan

Saat berintegrasi dengan hal-hal penting seperti plugin jQuery atau API DOM mentah, beberapa hal buruk mungkin diharapkan. Yang mengatakan saya masih mengharapkan Anda untuk dapat mengkonsolidasikan efek sedikit lebih dalam contoh itu.


Semoga saya tidak melupakan siapa pun! Beri tahu saya jika saya melakukannya atau jika ada sesuatu yang tidak jelas. Kami akan segera mencoba mengubah pelajaran dari ini menjadi beberapa dokumen.

Semua 111 komentar

Contoh ini memiliki balasan: https://github.com/facebook/react/issues/14920#issuecomment -466145690

KodeSandbox

Ini adalah komponen Kotak Centang yang tidak terkontrol yang menggunakan prop defaultIndeterminate untuk menetapkan status tak tentu pada render awal (yang hanya dapat dilakukan di JS menggunakan referensi karena tidak ada atribut elemen indeterminate ). Prop ini dimaksudkan untuk berperilaku seperti defaultValue , di mana nilainya hanya digunakan pada render awal.

Aturan mengeluh bahwa defaultIndeterminate hilang dari larik dependensi, tetapi menambahkannya akan menyebabkan komponen salah menimpa status tidak terkendali jika nilai baru dilewatkan. Array dependensi tidak dapat dihapus seluruhnya karena akan menyebabkan status tak tentu dikontrol sepenuhnya oleh prop.

Saya tidak melihat cara untuk membedakan antara ini dan jenis kasus yang aturannya _dimaksudkan_ untuk ditangkap, tetapi akan lebih bagus jika dokumentasi aturan akhir dapat menyertakan solusi yang disarankan. πŸ™‚

Re: https://github.com/facebook/react/issues/14920#issuecomment -466144466

@billyjanitsch Apakah ini akan berhasil? https://codesandbox.io/s/jpx1pmy7ry

Saya menambahkan useState untuk indeterminate yang diinisialisasi ke defaultIndeterminate . Efeknya kemudian menerima [indeterminate] sebagai argumen. Saat ini Anda tidak mengubahnya β€” tetapi jika Anda melakukannya nanti, saya rasa itu akan berhasil juga? Jadi kode mengantisipasi kemungkinan kasus penggunaan di masa depan sedikit lebih baik.

Jadi saya mendapatkan kasus (tepi?) berikut ini di mana saya meneruskan beberapa html dan menggunakannya dengan dangerouslySetInnerHtml untuk memperbarui komponen saya (beberapa konten editorial).
Saya tidak menggunakan prop html tetapi referensi di mana saya dapat menggunakan ref.current.querySelectorAll untuk melakukan keajaiban.
Sekarang saya perlu menambahkan html ke dependensi saya di useEffect meskipun saya tidak menggunakannya secara eksplisit. Apakah ini kasus penggunaan di mana saya sebenarnya harus menonaktifkan aturan?

Idenya adalah untuk mencegat semua klik tautan dari konten editorial dan melacak beberapa analitik atau melakukan hal-hal relevan lainnya.

Ini adalah contoh yang dipermudah dari komponen nyata:
https://codesandbox.io/s/8njp0pm8v2

Saya menggunakan react-redux, jadi ketika menurunkan pembuat tindakan di alat peraga dari mapDispatchToProps , dan menggunakan pembuat tindakan itu di sebuah kait, saya mendapatkan peringatan exhaustive-deps .

Jadi saya jelas dapat menambahkan tindakan redux ke array dependensi, tetapi karena tindakan redux adalah fungsi dan tidak pernah berubah, ini tidak perlu kan?

const onSubmit = React.useCallback(
  () => {
    props.onSubmit(emails);
  },
  [emails, props]
);

Saya berharap lint memperbaiki deps menjadi [emails, props.onSubmit] , tetapi saat ini lint selalu memperbaiki deps menjadi [emails, props] .

  1. CodeSandbox yang menunjukkan contoh kode minimal yang masih mengekspresikan maksud Anda (bukan "bilah foo" tetapi pola UI aktual yang Anda terapkan).

https://codesandbox.io/s/xpr69pllmz

  1. Penjelasan tentang langkah -

Seorang pengguna akan menambahkan email dan mengundang email tersebut ke aplikasi. Saya sengaja menghapus sisa UI hanya ke button karena tidak relevan dengan masalah saya.

  1. Penjelasan tentang API yang dimaksud dari Hook/komponen Anda.

Komponen saya memiliki satu prop, onSubmit: (emails: string[]) => void . Itu akan dipanggil dengan status emails ketika pengguna mengirimkan formulir.


EDIT: Dijawab di https://github.com/facebook/react/issues/14920#issuecomment -467494468

Ini karena secara teknis props.foo() meneruskan props itu sendiri sebagai this ke foo call. Jadi foo mungkin secara implisit bergantung pada props . Kami membutuhkan pesan yang lebih baik untuk kasus ini. Praktik terbaik selalu merusak.

KodeSandbox

Itu tidak menganggap bahwa pemasangan dan pembaruan dapat sangat berbeda ketika mengintegrasikan ketika lib pihak ke-3. Efek pembaruan tidak dapat disertakan dalam mount (dan menghapus array sama sekali), karena instance tidak boleh dimusnahkan pada setiap render

Hai,

Tidak yakin apa yang salah dengan kode saya di sini:

const [client, setClient] = useState(0);

useEffect(() => {
    getClient().then(client => setClient(client));
  }, ['client']);

Saya mendapat React Hook useEffect has a complex expression in the dependency array. Extract it to a separate variable so it can be statically checked

@joelmoss @sylvainbaronnet Saya menghargai umpan balik Anda, tetapi saya menyembunyikan komentar Anda karena mereka tidak menyertakan informasi yang kami minta di bagian atas masalah. Itu membuat diskusi ini tidak perlu sulit bagi semua orang karena ada konteks yang hilang. Saya akan dengan senang hati melanjutkan percakapan jika Anda memposting lagi dan menyertakan semua informasi yang relevan (lihat postingan teratas). Terima kasih atas pengertian.

  1. KodeSandbox
  2. Pengguna dapat memilih nama depan dan memaksa nama belakang untuk berubah. Pengguna dapat memilih nama belakang dan tidak memaksa nama depan untuk berubah.
  3. Ada kait khusus yang mengembalikan sebagian status, komponen pilih yang memperbarui status itu, dan metode pembaruan kait status yang memperbarui status secara terprogram. Seperti yang ditunjukkan, saya tidak selalu ingin menggunakan fungsi updater jadi saya membiarkannya menjadi item terakhir dalam array yang dikembalikan.

Saya percaya kode apa adanya seharusnya tidak salah. Itu sekarang pada baris 35, mengatakan bahwa setLastName harus dimasukkan dalam array. Adakah pemikiran tentang apa yang harus dilakukan tentang itu? Apakah saya melakukan sesuatu yang tidak terduga?

Saya benar-benar mengerti, dan saya biasanya akan melakukan semua itu untuk Anda, tetapi dalam kasus khusus saya, tidak ada kode yang unik bagi saya. Ini hanyalah pertanyaan tentang apakah penggunaan fungsi yang didefinisikan di luar hook (yaitu pembuat tindakan redux) dan digunakan di dalam hook harus mengharuskan fungsi tersebut ditambahkan sebagai hook dep.

Senang membuat kode dan kotak jika Anda masih membutuhkan info lebih lanjut. Terima kasih

@joelmoss Saya pikir repro saya mencakup kasus Anda juga.

Ya, CodeSandbox masih akan membantu. Bayangkan bagaimana rasanya beralih konteks di antara cuplikan kode orang sepanjang hari. Ini adalah beban mental yang besar. Tak satu pun dari mereka terlihat sama. Ketika Anda perlu mengingat bagaimana orang menggunakan pembuat tindakan di Redux atau konsep lain di luar React, itu bahkan lebih sulit. Masalahnya mungkin terdengar jelas bagi Anda tetapi sama sekali tidak jelas bagi saya apa yang Anda maksud.

@gaearon Saya mengerti itu masuk akal, sebenarnya itu berhasil untuk saya karena mengapa saya ingin mencapainya adalah:

Jika Anda ingin menjalankan efek dan membersihkannya hanya sekali (saat memasang dan melepas), Anda dapat meneruskan array kosong ([]) sebagai argumen kedua.

Terima kasih banyak atas klarifikasinya. (dan maaf untuk off topic)

Inilah versi kode dari apa yang saya terapkan. Tidak terlihat banyak, tetapi polanya 100% identik dengan kode asli saya.

https://codesandbox.io/s/2x4q9rzwmp?fontsize=14

  • Penjelasan tentang langkah -

Semuanya bekerja dengan baik dalam contoh ini! Kecuali masalah linter.

  • Penjelasan tentang API yang dimaksud dari Hook/komponen Anda.

Saya punya beberapa file seperti ini, di mana saya menjalankan fungsi dalam efeknya, tetapi saya hanya ingin menjalankannya setiap kali beberapa kondisi terpenuhi - misalnya, mengubah Id seri. Saya tidak ingin memasukkan fungsi dalam array.

Inilah yang saya dapatkan dari linter saat menjalankannya di kode kotak pasir:

25:5   warning  React Hook useEffect has a missing dependency: 'fetchPodcastsFn'. Either include it or remove the dependency array  react-hooks/exhaustive-deps

Pertanyaan saya adalah: apakah ini bagaimana seharusnya berperilaku (baik aturan linter, atau aturan array kait)? Apakah ada cara yang lebih idiomatis untuk menggambarkan efek ini?

@svenanders , saya ingin tahu tentang alasan mengapa Anda tidak ingin memasukkan fetchPodcastsFn ? Apakah karena Anda menemukannya berubah pada setiap render? Jika ya, Anda mungkin ingin memoize fungsi itu atau membuatnya statis (jika tidak memiliki parameter apa pun)

Untuk satu - ini tentang kejelasan. Ketika saya melihat fungsinya, saya ingin dengan mudah memahami kapan harus diaktifkan. Jika saya melihat _one_ id dalam array, itu sangat jelas. Jika saya melihat id itu dan banyak fungsi, itu menjadi lebih kacau. Saya harus menghabiskan waktu dan tenaga, bahkan mungkin men-debug fungsi, untuk memahami apa yang terjadi.
Fungsi saya tidak berubah saat runtime, jadi saya tidak tahu apakah memo itu penting (yang ini khususnya adalah tindakan pengiriman yang memicu epik, yang pada akhirnya menghasilkan perubahan status).

https://codesandbox.io/s/4xym4yn9kx

  • Langkah

Pengguna mengakses rute di halaman, tetapi mereka bukan pengguna super, jadi kami ingin mengarahkan mereka keluar dari halaman. props.navigate disuntikkan melalui perpustakaan router jadi kami sebenarnya tidak ingin menggunakan window.location.assign untuk mencegah halaman dimuat ulang.

Semuanya bekerja!

  • API yang Ditujukan

Saya memasukkan dependensi dengan benar seperti pada kotak pasir kode, tetapi linter memberi tahu saya bahwa daftar dependensi harus memiliki props alih-alih props.navigate . Mengapa demikian?

Tweet dengan tangkapan layar! https://twitter.com/ferdaber/status/1098966716187582464

EDIT: salah satu alasan yang valid ini bisa menjadi buggy adalah jika navigate() adalah metode yang bergantung pada terikat this , dalam hal ini secara teknis jika ada sesuatu di dalam props berubah maka hal-hal di dalamnya this juga akan berubah.

CodeSandbox: https://codesandbox.io/s/711r1zmq50

API yang dimaksud:

Hook ini memungkinkan Anda untuk mendebounce nilai yang berubah dengan cepat. Nilai debounce hanya akan mencerminkan nilai terbaru saat hook useDebounce belum dipanggil untuk jangka waktu yang ditentukan. Saat digunakan bersama dengan useEffect, seperti yang kami lakukan dalam resep, Anda dapat dengan mudah memastikan bahwa operasi yang mahal seperti panggilan API tidak terlalu sering dijalankan.

Langkah:

Contoh ini memungkinkan Anda untuk mencari Marvel Comic API dan menggunakan useDebounce untuk mencegah panggilan API diaktifkan pada setiap penekanan tombol.

IMO menambahkan "semuanya" yang kami gunakan dalam array dependensi tidak efisien.

Misalnya pertimbangkan useDebounce Hook . Dalam penggunaan dunia nyata (setidaknya di kami), delay tidak akan berubah setelah render pertama, tetapi kami memeriksa apakah itu berubah atau tidak pada setiap render ulang. Jadi, di kait ini, argumen kedua dari useEffect lebih baik menjadi [value] daripada [value, delay] .

Tolong jangan berpikir cek kesetaraan yang dangkal sangat murah. Mereka dapat membantu aplikasi Anda ketika ditempatkan secara strategis tetapi hanya dengan membuat setiap komponen menjadi murni sebenarnya dapat membuat aplikasi Anda lebih lambat. Pengorbanan.

Saya pikir menambahkan semuanya dalam array dependensi, memiliki masalah kinerja yang sama (atau bahkan lebih buruk) seperti menggunakan komponen murni di mana-mana. Karena, ada banyak skenario di mana kita tahu bahwa beberapa nilai yang kita gunakan tidak akan berubah, jadi kita tidak boleh menambahkannya dalam array dependensi, karena seperti yang dikatakan @gaearon , pemeriksaan kesetaraan yang dangkal tidak terlalu murah dan ini bisa membuat aplikasi kami lebih lambat.

Saya memiliki beberapa umpan balik tentang mengaktifkan aturan ini untuk bekerja secara otomatis pada kait khusus berdasarkan konvensi.

Saya dapat melihat dalam kode sumber bahwa ada beberapa maksud untuk memungkinkan orang menentukan regex untuk menangkap kait khusus dengan nama:

https://github.com/facebook/react/blob/ba708fa79b3db6481b7886f9fdb6bb776d0c2fb9/packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.js#L490 -L492

Apa yang akan tim React pikirkan tentang konvensi penamaan tambahan untuk kait khusus dengan array ketergantungan? Hooks sudah mengikuti konvensi yang diawali dengan use agar dapat dideteksi sebagai hook oleh plugin ini. Konvensi yang akan saya usulkan untuk mendeteksi kait khusus yang bergantung pada dependensi tertentu adalah semacam postfix, seperti WithDeps , yang berarti nama kait khusus lengkap dapat berupa useCustomHookWithDeps . Postfix WithDeps akan memberi tahu plugin bahwa argumen array terakhir adalah salah satu dependensi.

Plugin ini masih dapat mendukung regex tambahan, tetapi saya pikir saya akan melihat manfaat besar dengan mengizinkan penulis perpustakaan untuk mengekspor kait kustom mereka setelah diperbaiki dengan WithDeps daripada memaksa konsumen perpustakaan untuk secara eksplisit mengonfigurasi plugin untuk setiap dan semua kustom kait pihak ke-3 atau sebaliknya.

Ini memperingatkan dan menghapus secara otomatis pemeriksaan kesetaraan khusus.

const myEqualityCheck = a => a.includes('@');

useCallback(
  () => {
    // ...
  },
  [myEqualityCheck(a)]
);

Untuk useEffect, saya rasa peringatan "ketergantungan yang tidak perlu" tidak akan muncul karena "dependensi" itu akan mengubah cara efek menyala.

Katakanlah saya memiliki dua penghitung, induk dan anak:

  • Penghitung dapat ditambah/dikurangi secara independen.
  • Saya ingin mengatur ulang penghitung anak ke nol ketika penghitung induk berubah.

Penerapan:

  const [parent, setParent] = useState(0);
  const [child, setChild] = useState(0);

  useEffect(
    () => {
      setChild(0);
    },
    [parent] // "unnecessary dependency: 'parent'"
  );

Aturan exhaustive-deps memberikan peringatan React Hook useEffect has an unnecessary dependency: 'parent'. Either exclude it or remove the dependency array.

screen shot 2019-02-25 at 11 06 40 am

Saya tidak berpikir linter harus membuat saran ini karena itu mengubah cara aplikasi berperilaku.

  1. CodeSandbox: https://codesandbox.io/s/ol6r9plkv5
  2. Langkah-langkah: Ketika parent berubah, child reset ke nol.
  3. Niat: Alih-alih menandai ketergantungan sebagai tidak perlu, linter harus mengenali bahwa parent mengubah perilaku useEffect dan tidak menyarankan saya untuk menghapusnya.

[EDIT]

Sebagai solusinya, saya bisa menulis sesuatu seperti

const [parent, setParent] = useState(0)
const [child, setChild] = useState(0)
const previousParent = useRef(parent)

useEffect(() => {
  if (parent !== previousParent.current) {
    setChild(0)
  }

  previousParent.current = parent
}, [parent])

Tapi ada "puisi" dari cuplikan asli yang saya suka.

Saya pikir pertanyaannya adalah apakah kita harus menafsirkan array dependensi hanya sebagai mekanisme untuk optimasi kinerja vs perilaku komponen aktual. FAQ React Hooks menggunakan kinerja sebagai contoh mengapa Anda akan menggunakannya, tetapi saya tidak menafsirkan contoh yang berarti Anda hanya boleh menggunakan larik dependensi untuk meningkatkan kinerja, alih-alih saya melihatnya sebagai cara mudah untuk melewatkan panggilan efek secara umum.

Ini sebenarnya pola yang saya gunakan beberapa kali, untuk membatalkan beberapa cache internal:

useEffect(() => {
  if (props.invalidateCache) {
    cache.clear()
  }
}, [props.invalidateCache])

Jika saya seharusnya tidak menggunakannya dengan cara ini, silakan beri tahu saya dan abaikan komentar ini.

@MrLeebo
Bagaimana dengan

  const [parent, setParent] = useState(0);
  const [child, setChild] = useState(0);
  const updateChild = React.useCallback(() => setChild(0), [parent]);

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

Saya juga tidak akan mengerti potongan Anda. Ketergantungan hanya dapat diasumsikan berdasarkan kode Anda, tetapi itu mungkin bug. Meskipun proposal saya tidak selalu menyelesaikannya, itu membuatnya setidaknya lebih eksplisit.

Sepertinya ini sebelumnya diselesaikan melalui getDerivedStateFromProps ? Apakah Bagaimana cara menerapkan getDerivedStateFromProps? Tolong?

@nghiepit Saya menyembunyikan komentar Anda karena Anda mengabaikan daftar periksa yang diperlukan di posting pertama (misalnya CodeSandbox). Silakan ikuti daftar periksa dan posting lagi. Terima kasih.

@eps1lon Anda akan memiliki peringatan yang sama persis pada useCallback , dan saya tidak setuju dengan pola itu secara umum yang merupakan peningkatan dari aslinya, tetapi saya tidak ingin menggagalkan topik untuk membicarakannya. Untuk memperjelas, saya pikir aturan ketergantungan yang tidak perlu harus dilonggarkan untuk useEffect atau useLayoutEffect secara khusus, karena mereka dapat berisi logika yang efektif, tetapi aturan harus tetap berlaku untuk useCallback , useMemo , dll.

Saya mengalami beberapa masalah yang sama/pertanyaan model mental yang telah dijelaskan oleh @MrLeebo di sini. Perasaan saya adalah bahwa aturan ketergantungan useEffect tidak boleh seketat itu. Saya memiliki contoh yang sangat dibuat-buat yang sedang saya kerjakan untuk bukti dasar ide konsep. Saya tahu kode ini menyebalkan dan idenya tidak terlalu berguna, tetapi saya pikir ini adalah ilustrasi yang bagus tentang masalah yang dihadapi. Saya pikir @MrLeebo mengungkapkan pertanyaan yang saya alami dengan cukup baik:

Saya pikir pertanyaannya adalah apakah kita harus menafsirkan array dependensi hanya sebagai mekanisme untuk optimasi kinerja vs perilaku komponen aktual. FAQ React Hooks menggunakan kinerja sebagai contoh mengapa Anda akan menggunakannya, tetapi saya tidak menafsirkan contoh yang berarti Anda hanya boleh menggunakan larik dependensi untuk meningkatkan kinerja, alih-alih saya melihatnya sebagai cara mudah untuk melewatkan panggilan efek secara umum.

https://codesandbox.io/s/5v9w81j244

Saya secara khusus akan melihat kait useEffect di useDelayedItems . Saat ini menyertakan properti items dalam larik dependensi akan menyebabkan kesalahan linting, tetapi menghapus properti atau larik itu sepenuhnya memberi Anda perilaku yang tidak Anda inginkan.

Ide dasar dari useDelayedItems hook diberikan array item dan objek konfigurasi (penundaan dalam ms, ukuran halaman awal), saya awalnya akan diberikan subset item tergantung pada ukuran halaman yang dikonfigurasi. Setelah config.delay berlalu, item sekarang akan menjadi set lengkap item. Ketika diserahkan satu set item baru, kait harus menjalankan kembali logika "penundaan" ini. Idenya adalah versi yang sangat kasar dan bodoh dari rendering daftar besar yang tertunda.

Terima kasih atas semua pekerjaan pada aturan ini, ini sangat membantu saat mengembangkan. Bahkan jika contoh saya memiliki kualitas yang dipertanyakan, saya harap ini memberikan wawasan tentang bagaimana operator ini dapat ab/digunakan.

EDIT : Saya telah menambahkan beberapa komentar klarifikasi di dalam kode dan kotak di sekitar niat perilaku. Saya harap informasinya cukup, tetapi beri tahu saya jika ada yang masih membingungkan.

Bagaimana dengan satu nilai yang menggabungkan nilai-nilai lain?

Contoh: fullName diturunkan dari firstName dan lastName . Kami ingin memicu efek hanya ketika fullName berubah (seperti ketika pengguna menekan "Simpan") tetapi juga ingin mengakses nilai yang dibuatnya dalam efek

Demo

Menambahkan firstName atau lastName ke dependensi akan merusak banyak hal karena kita hanya ingin menjalankan efek setelah fullName berubah.

@aweary Saya tidak yakin nilai apa yang Anda dapatkan dari tipuan perubahan prop useEffect . Tampaknya onClick harus menangani "efek" itu.

https://codesandbox.io/s/0m4p3klpyw

Sejauh nilai tunggal yang menggabungkan nilai lain, useMemo mungkin akan menjadi apa yang Anda inginkan. Sifat perhitungan yang tertunda dalam contoh Anda berarti tidak memetakan persis 1:1 dengan perilaku tertaut Anda.

Saya akan membuat kode dan tautan kotak untuk contoh-contoh ini dan mengedit posting ini.

Saya memiliki aturan yang sangat sederhana: jika tab saat ini berubah, gulir ke atas:
https://codesandbox.io/s/m4nzvryrxj

useEffect(() => {
    window.scrollTo(0, 0);
  }, [activeTab]);

Dan saya mendapat React Hook useEffect has an unnecessary dependency: 'activeTab'. Either exclude it or remove the dependency array

Juga, ketika saya memigrasi beberapa komponen, saya ingin mereplikasi perilaku componentDidMount:

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

aturan ini mengeluh keras tentang ini. Saya ragu untuk menambahkan setiap ketergantungan ke array useEffect.

Terakhir, jika Anda mendeklarasikan fungsi inline baru sebelum useEffect, seperti ini:
https://codesandbox.io/s/nr7wz8qp7l

const foo = () => {...};
useEffect(() => {
    foo();
  }, []);

Anda mendapatkan: React Hook useEffect has a missing dependency: 'foo'. Either include it or remove the dependency array
Saya tidak yakin bagaimana perasaan saya tentang menambahkan foo ke daftar dependensi. Di setiap render foo adalah fungsi baru dan useEffect akan selalu dijalankan?

@ksaldana1 menempatkan efek samping di dalam event handler tidak cukup. Itu akan menyebabkan efek samping terjadi sebelum pembaruan aktual dilakukan, yang mungkin menyebabkan efek samping terpicu lebih sering daripada yang Anda inginkan.

Ini juga tidak akan berfungsi saat menggunakan useReducer karena status yang diperbarui tidak akan tersedia di dalam event handler.

Sejauh nilai tunggal yang menggabungkan nilai lain, useMemo mungkin akan menjadi apa yang Anda inginkan.

Jika contoh ini menggunakan useMemo itu akan rusak karena useMemo akan menghasilkan fullName setiap kali firstName atau lastName diubah. Perilaku di sini adalah fullName tidak diperbarui sampai tombol Simpan diklik.

@aweary Apakah sesuatu seperti ini akan berhasil? https://codesandbox.io/s/lxjm02j50m
Saya sangat ingin tahu apa implementasi yang disarankan.

Saya juga ingin tahu lebih banyak tentang beberapa hal yang Anda nyatakan. Bisakah Anda mengarahkan saya ke info lebih lanjut tentang ini?

Pemicu dan efek di handler:

yang akan menyebabkan efek samping terjadi sebelum pembaruan aktual dilakukan, yang mungkin menyebabkan efek samping terpicu lebih sering daripada yang Anda inginkan.

Menggunakan status useReducer di event handler:

status yang diperbarui tidak akan tersedia.

Terima kasih!

@bugzpodder agak tidak terkait tetapi panggilan gulir harus berada di dalam useLayoutEffect alih-alih useEffect . Saat ini ada kedipan yang terlihat saat menavigasi ke rute baru

Tidak yakin apakah ini disengaja atau tidak:

Saat Anda memanggil fungsi dari props, linter menyarankan untuk menambahkan seluruh objek props sebagai dependensi.

Versi pendek:

useEffect(function() {
  props.setLoading(true)
}, [props.setLoading])

// ⚠️ React Hook useEffect has a missing dependency: 'props'.

Versi lengkap: Codesandbox

Mencoba lagi... tapi kali ini lebih sederhana ;)

Saat menurunkan fungsi di alat peraga, atau memang dari mana saja, dan menggunakan fungsi itu di dalam kait, saya mendapatkan peringatan exhaustive-deps .

Jadi saya jelas dapat menambahkan fungsi ke array dependensi, tetapi karena itu adalah fungsi dan tidak pernah berubah, ini tidak perlu kan?

-> Kotak pasir

Semoga itu semua yang Anda butuhkan, tetapi saya hanya memotong kotak pasir

Terima kasih.

Jadi saya jelas dapat menambahkan fungsi ke array dependensi, tetapi karena itu adalah fungsi dan tidak pernah berubah, ini tidak perlu kan?

Ini tidak benar; fungsi dapat berubah setiap saat ketika induk merender ulang.

@sidharthkp

Saat Anda memanggil fungsi dari props, linter menyarankan untuk menambahkan seluruh objek props sebagai dependensi.

Ini karena secara teknis props.foo() meneruskan props itu sendiri sebagai this ke panggilan foo . Jadi foo mungkin secara implisit bergantung pada props . Kami membutuhkan pesan yang lebih baik untuk kasus ini. Praktik terbaik selalu merusak.

fungsi dapat berubah setiap saat ketika induk merender ulang.

Tentu, tetapi jika fungsinya didefinisikan di tempat lain dan bukan dari komponen induk, itu tidak akan pernah berubah.

Tentu, tetapi jika fungsinya didefinisikan di tempat lain dan bukan dari komponen induk, itu tidak akan pernah berubah.

Jika Anda mengimpornya secara langsung, aturan lint tidak akan meminta Anda untuk menambahkannya ke deps efek. Hanya jika itu dalam lingkup render. Jika dalam lingkup render maka aturan lint tidak tahu dari mana asalnya. Bahkan jika tidak dinamis hari ini, bisa jadi besok ketika seseorang mengubah komponen induk. Jadi menentukannya adalah default yang tepat. Tidak ada salahnya untuk menentukan apakah itu statis.

thx @gaearon

Ini karena secara teknis props.foo() meneruskan props itu sendiri sebagai this ke panggilan foo . Jadi foo mungkin secara implisit bergantung pada props . Kami membutuhkan pesan yang lebih baik untuk kasus ini. Praktik terbaik selalu merusak.

Itu menjawab pertanyaan saya juga. Terima kasih! πŸ˜„

https://codesandbox.io/s/98z62jkyro

Jadi saya membuat perpustakaan untuk menangani validasi input dengan mendaftarkan semua input menggunakan api yang diekspos dalam konteks untuk setiap input untuk mendaftarkan diri. Saya membuat kait khusus yang disebut useRegister.

Mantan:

 useEffect(
    () => {
      register(props.name, props.rules || []);
      console.log("register");
      return function cleanup() {
        console.log("cleanup");
        unregister(props.name);
      };
    },
    [props.name, props.rules, register, unregister]
  );

Saat memaksa props.rules untuk menjadi bagian dari dependensi, tampaknya berakhir dengan render-loop tak terbatas. Props.rules adalah larik fungsi untuk didaftarkan sebagai validator. Saya hanya mendeteksi masalah ini ketika menyediakan array sebagai dependensi. Di kotak dan kode saya, Anda dapat melihatnya membuka konsol.

Hanya memiliki props.name sebagai dependensi membuatnya berfungsi sebagaimana dimaksud. Menegakkan dependensi akan seperti yang ditunjukkan lainnya mengubah perilaku aplikasi, dan dalam hal ini efek sampingnya parah.

@bugzpodder

Re: https://github.com/facebook/react/issues/14920#issuecomment -467212561

useEffect(() => {
    window.scrollTo(0, 0);
  }, [activeTab]);

Ini sepertinya kasus yang sah. Saya akan melonggarkan peringatan untuk mengizinkan deps asing hanya untuk efek. (Tapi tidak untuk useMemo atau useCallback .)

Juga, ketika saya memigrasi beberapa komponen, saya ingin mereplikasi perilaku componentDidMount:
aturan ini mengeluh keras tentang ini. Saya ragu untuk menambahkan setiap ketergantungan ke array useEffect.

Maaf, Anda tidak menambahkan contoh apa pun untuk itu sehingga kami kehilangan kesempatan untuk membahasnya. Itulah mengapa pos OP meminta untuk menentukan contoh UI konkret . Anda melakukannya untuk poin pertama tetapi tidak untuk yang ini. Saya senang mendiskusikannya ketika Anda menambahkan contoh konkret untuk itu. Spesifiknya sangat bergantung padanya.

Terakhir, jika Anda mendeklarasikan fungsi inline baru sebelum useEffect, seperti ini: https://codesandbox.io/s/nr7wz8qp7l

Dalam hal ini perbaikan mudah adalah memindahkan doSomething ke dalam efek. Maka Anda tidak perlu menyatakannya. Atau, Anda dapat useCallback sekitar doSomething . Saya terbuka untuk melonggarkan aturan untuk mengizinkan penghilangan fungsi yang hanya menggunakan deps yang dideklarasikan. Tapi ini bisa membingungkan jika Anda memiliki fungsi yang memanggil fungsi lain, dan menambahkan prop atau status ke salah satu fungsi tersebut. Tiba-tiba semua efek deps menggunakannya secara transitif harus diperbarui. Ini bisa membingungkan.

Saya tidak tahu apakah ini permintaan fitur untuk aturan baru, atau sesuatu yang dapat ditingkatkan tentang exhaustive-deps …

import React from 'react'
import emitter from './thing'

export const Foo = () => {
  const onChange = () => {
    console.log('Thing changed')
  }

  React.useEffect(() => {
    emitter.on('change', onChange)
    return () => emitter.off('change', onChange)
  }, [onChange])

  return <div>Whatever</div>
}

Karena fungsi onChange dibuat setiap render, argumen useEffect hook [onChange] berlebihan, dan mungkin juga dihapus:

   React.useEffect(() => {
     emitter.on('change', onChange)
     return () => emitter.off('change', onChange)
-  }, [onChange])
+  })

Linter dapat mendeteksi itu, dan menyarankan Anda untuk menghapus argumen array.

Ada situasi di mana saya telah mempertahankan daftar item array, hanya untuk menyadari satu atau lebih dari mereka sedang dibuat dan tetap membatalkan hook setiap render.

Baru saja merilis [email protected] dengan beberapa perbaikan dan pesan yang lebih baik untuk aturan ini. Tidak ada terobosan tetapi beberapa kasus harus diselesaikan. Saya akan melihat sisanya minggu depan.

Saya juga memposting langkah pertama yang mungkin untuk menghilangkan deps fungsi "aman" di sini: https://github.com/facebook/react/pull/14996 . (Lihat tes.) Jika Anda memiliki ide tentang heuristik yang berguna dan bagaimana memandu orang untuk memperbaiki yang benar, silakan beri komentar di PR.

@gaearon Ide bagus. Ini pasti akan berguna untuk memiliki gaya yang lebih baik saat menggunakan kait πŸ™

@gaearon Masih tidak berfungsi jika aksinya berasal dari prop. Pertimbangkan contoh ini:

image

Di mana setScrollTop adalah tindakan redux.

Dalam contoh ini dalam komponen Slider , saya menggunakan useEffect untuk menunggu hingga DOM tersedia sehingga saya dapat memasang komponen noUiSlider. Karena itu saya meneruskan [sliderElement] untuk memastikan bahwa referensi tersedia di DOM ketika efeknya berjalan. Kami server merender komponen kami juga, jadi ini juga memastikan DOM tersedia sebelum rendering. Alat peraga lain yang saya gunakan di useEffect (yaitu min, maks, onUpdate, dll) adalah konstanta dan oleh karena itu saya tidak melihat perlunya diteruskan ke efek.

screen shot 2019-03-02 at 5 17 09 pm


Inilah efeknya seperti yang terlihat di kode dan kotak:

const { min, max, step } = props;
const sliderElement = useRef();

useEffect(() => {
  if (!sliderElement.current) {
    return;
  }

  const slider = sliderElement.current;
  noUiSlider.create(slider, {
    connect: true,
    start: [min, max],
    step,
    range: {
      min: [min],
      max: [max],
    },
  });

  if (props.onUpdate) {
    slider.noUiSlider.on('update', props.onUpdate);
  }

  if (props.onChange) {
    slider.noUiSlider.on('change', props.onChange);
  }
}, [sliderElement]);

@WebDeg-Brian Saya tidak dapat membantu Anda tanpa demo CodeSandbox lengkap. Maaf. Lihat posting teratas.

Saya memposting sedikit tentang kesalahpahaman umum "fungsi tidak pernah berubah":

https://overreacted.io/how-are-function-components-different-from-classes/

Topiknya tidak persis sama, tetapi relevan dengan aturan ini.

Hai @gaearon , Berikut adalah contoh yang Anda minta saya posting di sini (dari tweeter) :)

Pada dasarnya saya mencoba mengonversi jebakan reaksi perpustakaan saya menjadi kait.
Ini hanya jebakan untuk acara, di luar / di dalam elemen.

Masalah saya adalah jika useEffect tidak tergantung pada nilai status ( trapped ), terkadang basi.
Saya menulis beberapa komentar dan log untuk ditunjukkan. Lihat file useTrap.js , komentar dan log ada di fungsi useEffect dan preventDefaultHelper .

Sepengetahuan saya, jika suatu nilai tidak ada di dalam useEffect maka itu tidak boleh menjadi bagian dari dependensinya (koreksi saya jika saya salah).

  1. Kode Sandbox
  2. Langkah:
    Pengguna mengklik di dalam kotak untuk mengaktifkannya, dan di luar untuk membuatnya tidak aktif, pengguna juga dapat mengklik dengan tombol kanan mouse, meskipun untuk klik pertama seharusnya tidak memicu menu konteks ( e.preventDefault ).
    Ketika saya mengatakan "klik pertama" yang saya maksud adalah klik pertama yang mengubah status.
    Diberikan kotak aktif, klik kanan di luarnya akan mengubah status menjadi "tidak aktif" dan mencegah menu konteks. klik lain di luar tidak akan memengaruhi status, maka menu konteks akan muncul.

Saya harap saya jelas di sini dan kasus penggunaannya dapat dimengerti, beri tahu saya jika saya perlu memberikan info lebih lanjut. Terima kasih!

Hai @gaearon , saya menambahkan di sini kait khusus saya seperti yang Anda sarankan di Twitter! Saya berjuang untuk menemukan bentuk yang tepat yang tidak melewatkan dependensi.

Ini rumit sebagai contoh, saya harap saya bisa menjelaskannya dengan cara yang jelas dan dapat dimengerti.

Ini adalah keadaan saat ini: react-async-utils/src/hooks/useAsyncData.ts

Ikhtisar fungsi
Membantu pengguna menangani panggilan asinkron, menghasilkan data dan statusnya sepanjang proses.

triggerAsyncData update asyncData menyatakan secara tidak sinkron menurut fungsi getData yang mengembalikan Promise . triggerAsyncData dapat dipanggil baik sebagai efek atau "secara manual" oleh pengguna hook.

Tantangan

  1. Ketergantungan efek yang memanggil triggerAsyncData adalah yang intrinsik. triggerAsyncData adalah ketergantungan efek, tetapi dibuat di setiap render. Garis pemikiran sejauh ini:

    1. Tambahkan saja sebagai dependensi => Tapi kemudian efeknya berjalan di setiap render.

    2. Tambahkan sebagai dependensi, tetapi gunakan useMemo / useCallback dengan triggerAsyncData => useMemo / useCallback hanya boleh digunakan untuk optimasi kinerja AFAIK.

    3. Lingkup di dalam efek => Lalu saya tidak bisa mengembalikannya ke pengguna.

    4. Alih-alih menggunakan triggerAsyncData sebagai dependensi, gunakan dependensi triggerAsyncData sebagai dependensi => Opsi terbaik yang saya temukan sejauh ini. Tapi itu melanggar aturan "exhaustive-deps".

  2. Setiap parameter input dari custom hook adalah/menjadi ketergantungan dari efek batin kita. Jadi fungsi inline dan objek literal sebagai parameter membuat efek berjalan terlalu sering.

    1. Serahkan tanggung jawab kepada pengguna. Mereka akan memberikan nilai yang sesuai, menggunakan useMemo / useCallback jika diperlukan => Saya sering kali tidak melakukannya. Dan jika mereka melakukannya cukup bertele-tele.

    2. Izinkan argumen tambahan untuk kait khusus untuk memberikan kedalaman input, dan gunakan itu alih-alih input itu sendiri => Keren, lebih sedikit verbose, lebih banyak kontrol untuk pengguna. Saya menggunakan ini. Tapi itu melanggar aturan "exhaustive-deps". (Sebenarnya menggunakan saya ini dan kembali ke deps biasa jika argumen tambahan tidak disediakan. Saya menemukan pola yang kuat).

  3. Ketergantungan yang dikelola dengan buruk untuk kait khusus menghasilkan loop asinkron tak terbatas karena efek dalam. Saya telah menggunakan pemrograman defensif untuk mencegah hal ini, tetapi menambahkan "palsu"? ketergantungan ( asyncData ). Ini melanggar aturan "exhaustive-deps" lagi.

Penjelasan lebih panjang dari yang saya harapkan ... tapi saya pikir itu mencerminkan perjuangan saya untuk menggunakan kait dengan cara yang benar. Tolong beri tahu saya jika ada hal lain yang bisa saya lakukan untuk membuat kesulitan ini lebih jelas.

Terima kasih untuk semua kerja keras di sini!

Hai @gaearon terima kasih atas semua kerja

  1. Contoh pengambilan data asinkron minimal Contoh CodeSandbox .

  2. Pengguna diharapkan melihat 5 string judul lorem ipsum yang diambil dari json api .

  3. Saya membuat kait khusus untuk pengambilan data dengan API yang dimaksud:

const { data, isLoading, isError } = useDataApi('https://jsonplaceholder.typicode.com/posts')

Internal kait useDataApi :

...
  const fetchData = async () => {
    let response;
    setIsError(false);
    setIsLoading(true);

    try {
      response = await fetch(url).then(response => response.json());

      setData(response);
    } catch (error) {
      setIsError(true);
    }

    setIsLoading(false);
  };

  useEffect(
    () => {
      fetchData();
    },
    [url]
  );
...

Masalahnya adalah kode ini

  useEffect(
    () => {
      fetchData();
    },
    [url]
  );

di mana react-hooks/exhaustive-deps mengeluarkan peringatan bahwa saya harus menambahkan fetchData ke larik dependensi saya dan url harus dihapus.

Jika saya mengubah kait ini menjadi

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

kemudian terus-menerus menembakkan permintaan dan tidak pernah berhenti, yang tentu saja merupakan masalah besar. Saya tidak yakin apakah kode saya bermasalah atau react-hooks/exhaustive-deps mengeluarkan false positive.

Setiap bantuan dihargai. Terima kasih banyak.

PS Saya membaca komentar Anda tentang useEffect tidak cocok untuk pengambilan data , namun, dokumen reaksi mengklaim bahwa Data fetching, setting up a subscription, and manually changing the DOM in React components are all examples of side effects yang memberi saya keyakinan bahwa data useEffect sangat bagus untuk pengambilan data. Jadi sekarang saya agak bingung

@jan-stehlik Anda harus membungkus fetchData dengan useCallback. saya memotong kode dan kotak Anda dengan perubahan yang diperlukan di sini https://codesandbox.io/s/pjmjxprp0m

Sangat membantu, terima kasih banyak @viankakrisna !

Sepengetahuan saya, jika suatu nilai tidak ada di dalam useEffect maka itu tidak boleh menjadi bagian dari dependensinya (koreksi saya jika saya salah).

Saya pikir Anda salah di sini. Atau lebih tepatnya, agak bingung. Anda menggunakan handleEvent dalam efek Anda. Tapi Anda tidak menyatakannya. Itu sebabnya nilai-nilai yang dibacanya basi.

Menarik.

Anda menggunakan handleEvent di dalam efek Anda. Tapi Anda tidak menyatakannya. Itu sebabnya nilai-nilai yang dibacanya basi.

Apa yang Anda maksud dengan: _"Tapi Anda tidak menyatakannya"_?
Itu dinyatakan di bawah efek (sama seperti semua penangan lainnya).

Atau maksud Anda karena pawang menggunakan nilai status, dan efeknya adalah memasang pawang maka itu berarti efeknya tergantung pada nilai status itu?

Atau maksud Anda karena pawang menggunakan nilai status, dan efeknya adalah memasang pawang maka itu berarti efeknya tergantung pada nilai status itu?

Ya, jika Anda menggunakan fungsi Anda harus baik menyatakan hal itu di deps (dan dalam kasus yang membungkusnya dengan useCallback untuk menghindari menciptakan itu), atau segala sesuatu yang menggunakan fungsi.

Oke ini berita buat saya! Terima kasih atas masukannya @gaearon :)
Saya hanya ingin itu menjadi sangat jelas bagi saya (dan orang lain?)...

Jika suatu efek memanggil, meneruskan, atau melakukan sesuatu dengan suatu fungsi, kita perlu meneruskan ke deps array-nya:
Fungsi itu sendiri ATAU Variabel yang digunakan fungsi ini.
Jika fungsi tersebut dideklarasikan di dalam function component / custom hook maka disarankan untuk membungkusnya dengan useCallback sehingga tidak akan dibuat ulang setiap kali komponen atau custom hook kita berjalan.

Saya harus mengatakan saya tidak melihatnya di dokumen .
Apakah menurut Anda boleh menambahkannya ke bagian _Note_?

Catatan
Array input tidak diteruskan sebagai argumen ke fungsi efek. Namun, secara konseptual, itulah yang mereka wakili: setiap nilai yang direferensikan di dalam fungsi efek juga harus muncul di array input. Di masa depan, kompiler yang cukup canggih dapat membuat array ini secara otomatis.

Sunting
Satu hal lagi, dalam contoh saya, apa deps untuk useCallback ketika membungkus handleEvent (atau penangan lain karena alasan itu). apakah itu event itu sendiri?

Saya harus mengatakan saya tidak melihatnya di dokumen.

Dokumen mengatakan "setiap nilai yang direferensikan di dalam fungsi efek juga harus muncul di array input". Fungsi adalah nilai juga. Saya setuju kita perlu mendokumentasikan ini dengan lebih baik β€” itulah inti dari utas ini :-) Saya mengumpulkan kasus penggunaan untuk halaman dokumentasi baru yang didedikasikan untuk ini.

Satu hal lagi, dalam contoh saya, apa deps untuk useCallback ketika membungkus handleEvent (atau penangan lain karena alasan itu). apakah itu acara itu sendiri?

Tidak yakin apa yang kamu maksud. Nilai apa pun yang dirujuk oleh fungsi di luarnya. Sama seperti di useEffect .

Saya kira saya tidak memikirkan ini dengan fungsi sebagai dependensi. model mental saya salah, saya berpikir seperti melewatkan fungsi sebagai ketergantungan hanya jika itu masuk sebagai penyangga atau argumen ke komponen/kait saya. Terima kasih telah mengklarifikasi ini untuk saya.

Adapun useCallback , saya menggunakannya seperti ini:

const memoHandleEvent = useCallback(
    handleEvent
);

dan tentu saja meneruskan memoHandleEvent sebagai dependensi untuk useEffect juga ke addEventListener pengganti fungsi handleEvent . sepertinya berhasil, semoga ini cara yang tepat dan idiomatis untuk melakukannya.

Catatan useCallback tanpa argumen kedua tidak melakukan apa-apa.

Sekali lagi, kotak pasir lengkap akan diperlukan. Saya tidak tahu apakah perbaikan Anda benar dengan deskripsi yang tidak lengkap seperti ini.

Catatan useCallback tanpa argumen kedua tidak melakukan apa-apa.

Argg! :meringis: lol

Sekali lagi, kotak pasir lengkap akan diperlukan. Saya tidak tahu apakah perbaikan Anda benar dengan deskripsi yang tidak lengkap seperti ini.

Ah, ini adalah tautan yang sama dari atas. saya baru update :)

Tolong jangan perbarui tautan CodeSandbox : PI membutuhkannya seperti aslinya β€” jika tidak, saya tidak dapat menguji aturan lint pada tautan tersebut. Bisakah Anda membuat dua kotak pasir terpisah jika Anda memiliki dua solusi berbeda? Jadi saya bisa memeriksa masing-masing.

Maaf! :PI melebihi jumlah kotak pasir di akun saya. izinkan saya menghapus beberapa dan saya akan membuat yang lain (dan mengembalikan perubahan ke aslinya).

@gaearon ini adalah tautan kedua ke solusi dengan useCallback

Saya memiliki skenario yang saya yakini baik-baik saja, tetapi linter mengeluh. sampel saya:

KodeSandbox

Apa yang coba diwakili oleh sampel adalah ketika pengguna mengklik tombol, kemudian permintaan dibuat untuk meminta data baru berdasarkan Id dan fungsi yang disediakan sebelumnya. Jika Id berubah, maka seharusnya tidak meminta data baru; hanya permintaan baru untuk memuat ulang yang akan memicu permintaan data baru.

Contoh di sini sedikit dibuat-buat. Dalam aplikasi saya yang sebenarnya, React tinggal di DIV yang merupakan bagian kecil dari aplikasi web yang jauh lebih besar. Fungsi yang diteruskan adalah melalui Redux dan mapDispatchToProps, di mana pembuatan tindakan mengambil Id dan membuat permintaan ajax untuk mengambil data dan memperbarui toko. Prop refreshRequest diteruskan melalui React.createElement. Dalam implementasi asli saya, saya memiliki kode yang terlihat seperti ini di komponen kelas:

componentDidUpdate (prevProps) { const { getData, someId, refreshRequest} = this.props; if (prevProps.refreshRequest!== this.props.refreshRequest) { getData(someId); } }

Saya mencoba menerapkan perilaku yang sama dengan kait efek. Tetapi seperti yang tertulis dalam sampel, linter mengeluh:

peringatan React Hook useEffect memiliki dependensi yang hilang: 'getData' dan 'someId'. Sertakan atau hapus larik ketergantungan

Jika saya menambahkan semua yang diinginkan linter, maka jika pengguna mengklik salah satu tombol dalam contoh useEffect dipicu. Tapi saya hanya ingin itu dipicu ketika tombol Minta data baru ditekan.

Semoga masuk akal. Saya akan senang untuk mengklarifikasi lebih lanjut, jika ada sesuatu yang tidak jelas. Terima kasih!

Saya baru saja menerbitkan [email protected] yang memiliki dukungan eksperimental untuk mendeteksi dependensi fungsi kosong (yang cenderung tidak terlalu berguna tanpa useCallback ). Berikut gifnya:

demo

Akan senang jika Anda bisa mencobanya dalam proyek Anda dan lihat bagaimana rasanya! (Silakan mengomentari aliran spesifik itu di https://github.com/facebook/react/pull/15026.)

Saya akan mencobanya pada contoh dari utas ini besok.

Saya belum mencobanya tetapi saya ingin tahu apakah ini berhubungan dengan pengangkatan. Ini hanyalah "contoh minimum" yang saya buat di tempat sehingga tidak berguna untuk apa pun, tetapi saya sering menggunakan deklarasi yang diangkat untuk membuatnya lebih mudah untuk melihat pernyataan return .

function Component() {
  useEffect(() => {
    handleChange
  }, [handleChange])

  return null

  function handleChange() {}
}

Akan lebih baik jika aturan memiliki opsi untuk mengonfigurasi fungsi lain agar berperilaku seperti useEffect sejauh menyangkut linter. Misalnya, saya menambahkan ini sehingga saya dapat dengan mudah menggunakan fungsi async untuk efek yang melakukan panggilan AJAX - namun, seperti ini saya kehilangan semua manfaat linting exhaustive-deps :

const useAsyncEffect = (fn, ...args) => {
    /* eslint-disable react-hooks/exhaustive-deps */
    useEffect(() => {
        fn();
    }, ...args);
};

Sunting: Tidak apa-apa, saya baru menyadari bahwa ini sudah dimungkinkan menggunakan opsi additionalHooks dari aturan.

Halo semua,
Saya punya contoh https://codesandbox.io/s/znnmwxol7l

Di bawah ini yang saya inginkan:

function App() {
  const [currentTime, setCurrentTime] = React.useState(moment());

  const currentMonth = React.useMemo(
    () => {
      console.log("RUN");
      return currentTime.format("MMMM");
    },
    [currentTime.format("MMMM")] // <= this proplem [currentTime]
  );

  return (
    <div className="App">
      <h1>Current month: {currentMonth}</h1>
      <div>
        <button
          onClick={() => setCurrentTime(currentTime.clone().add(1, "days"))}
        >
          + 1 day
        </button>
      </div>
      <div>{currentTime.toString()}</div>
    </div>
  );
}

Tapi inilah yang saya dapatkan:

  const currentMonth = React.useMemo(
    () => {
      console.log("RUN");
      return currentTime.format("MMMM");
    },
    [currentTime] // <= this proplem
  );

Kapan saja currentTime berubah maka currentMonth tidak perlu menghitung ulang

Atau entah bagaimana saya bisa melakukannya di bawah ini:

  const currentMonth = React.useMemo(
    () => {
      return currentTime.format("MMMM");
    },
    [myEqualityCheck(currentTime)]
  );

Maaf kalau ini sudah dijawab, tidak bisa melihatnya di utas di atas:
Cara menjalankan kait useEffect hanya di mount adalah dengan mendefinisikan array input kosong sebagai parameter kedua. Namun ketika melakukan itu, keluhan lengkap-deps tentang memasukkan argumen input tersebut, yang akan mengubah efeknya menjadi juga berjalan saat pembaruan.
Apa pendekatan untuk menjalankan useEffect hanya saat dipasang dengan deps lengkap diaktifkan?

@einarq Saya kira Anda perlu memastikan bahwa semua nilai yang dirujuk di useEffect on-mount Anda tidak akan pernah berubah. Itu bisa dicapai dengan menggunakan kait lain seperti useMemo . Setelah itu terlepas apakah aturan ESlint ini menambahkan semua referensi ke dalam array (dengan perbaikan otomatis) atau tidak, kode akan dieksekusi hanya sekali.

@einarq Seperti yang dinyatakan di tempat lain di utas, kami tidak dapat membantu Anda tanpa CodeSandbox. Karena jawabannya sangat tergantung pada kode Anda.

@nghiepit Contoh Anda tidak masuk akal bagi saya. Jika input Anda adalah currentTime.format("MMMM") maka useMemo tidak mengoptimalkan apa pun untuk Anda karena Anda sudah menghitungnya . Jadi, Anda hanya menghitungnya dua kali tanpa perlu.

Apakah mungkin untuk menentukan indeks argumen mana yang merupakan panggilan balik pada opsi additionalHooks ? Saya melihat bahwa kami mengasumsikan sekarang dalam kode bahwa itu akan menjadi yang pertama https://github.com/facebook/react/blob/9b7e1d1389e080d19e71680bbbe979ec58fa7389/packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.js#L1051 - L1081

Saat ini tidak mungkin. Panduan umum kami adalah bahwa Hooks kustom sebaiknya memilih untuk tidak memiliki argumen deps karena menjadi sangat sulit untuk memikirkan bagaimana deps menulis. Jika Anda mengambil suatu fungsi, Anda mungkin ingin meminta pengguna untuk useCallback sebagai gantinya.

@CarlosGines Bisakah Anda membuat CodeSandbox, seperti yang diminta di pos OP. Saya menanyakan ini karena suatu alasan β€” jika tidak, sulit bagi saya untuk memverifikasi perubahan. Terutama ketika itu ditulis dalam TypeScript.

Saya baru saja menerbitkan [email protected] dengan pesan yang semoga lebih bermanfaat dan heuristik yang lebih cerdas. Silakan mencobanya pada proyek Anda sebelum menjadi stabil.

Saya berharap bahwa dengan sebagian besar contoh di utas ini, itu harus memberikan saran yang masuk akal yang akan membantu Anda memperbaiki masalah.

haruskah itu [email protected] ?

Ya.

Sebenarnya baru saja menerbitkan [email protected] dengan beberapa perubahan kecil.

@gaearon Apakah Anda berhasil mendapatkan eslint untuk bekerja di dalam kode dan kotak kebetulan?

@einarq mungkin sesuatu seperti ini apakah ini akan berhasil?

const useDidMount = fn => {
  const callbackFn = useCallback(fn)
  useEffect(() => {
    callbackFn()
  }, [callbackFn])
}

Saya sebenarnya malu untuk mengatakan bahwa saya tidak bisa membuat plugin berfungsi pada aplikasi CRA sederhana (berdasarkan cuplikan saya ).
di sini adalah repo bercabang dari kode dan kotak.

Saya telah melihat catatan ini di dokumen :

Catatan: Jika Anda menggunakan Create React App, harap tunggu rilis skrip reaksi terkait yang menyertakan aturan ini alih-alih menambahkannya secara langsung.

Tetapi apakah tidak ada cara untuk mengujinya dengan CRA saat ini? πŸ‘‚

Ya, Anda harus mengeluarkan dan menambahkannya ke konfigurasi ESLint sampai kami menambahkannya di sana. Anda dapat mengeluarkan di cabang dan tidak menggabungkannya. :-)

Hai,

pertama-tama: Terima kasih banyak telah bekerja sangat erat dengan komunitas dan pekerjaan luar biasa yang Anda lakukan secara umum!

Kami memiliki masalah dengan kait khusus yang kami buat di sekitar Fetch API. Saya telah membuat Codesandbox untuk mendemonstrasikan masalah tersebut.

Contoh pada Codesandbox

https://codesandbox.io/s/kn0km7mzv

Catatan: Masalah (lihat di bawah) menghasilkan infinite loop dan saya tidak ingin DDoS jsonplaceholder.typicode.com yang saya gunakan untuk mendemonstrasikan masalah tersebut. Oleh karena itu, saya telah menyertakan pembatas permintaan sederhana menggunakan penghitung. Ini tidak diperlukan untuk menunjukkan masalahnya, tetapi rasanya salah untuk memecat permintaan dalam jumlah tak terbatas ke proyek hebat ini.

Penjelasan

Idenya adalah untuk mempermudah menangani 3 kemungkinan status permintaan API: Loading, Success, dan Error. Jadi kami telah membuat kait khusus useFetch() yang mengembalikan 3 properti isLoading , response dan error. It makes sure that either response or error is set and updates isLoading . As the name implies, it uses the Fetch` API.

Untuk melakukannya, ia menggunakan 3 kait useState :

  const [isLoading, setIsLoading] = useState(false);
  const [response, setResponse] = useState(null);
  const [error, setError] = useState(null);

dan pengait useEffect :

useEffect(
    () => {
      fetch(url, fetchConfig)
        .then(/* Handle fetch response... See Codesandbox for the actual implementation */)
    },
    [url]
  );

Ini berfungsi dengan baik selama array dependensi useEffect hanya berisi [url] . Jika kita menambahkan fetchConfig (= [url, fetchConfig] ), itu menghasilkan infinite loop. Untuk kasus penggunaan khusus kami, cukup jalankan kembali efeknya ketika url berubah tetapi linter tidak mengizinkan, gunakan hanya [url] (diuji dengan v1.4.0 dan v1.5.0-beta.1 ).

Di akhir kait khusus, 3 variabel status dikembalikan sebagai objek:

  return {
    error,
    isLoading,
    response
  };

Saya tidak yakin apakah ini tempat yang tepat untuk meminta panduan tentang masalah ini karena saran linting masuk akal, saya kira. Maaf jika tidak.

Bagaimana jika fetchConfig berubah? Mengapa Anda berharap itu statis?

Baiklah. Saya merilis [email protected] dengan perbaikan dan peningkatan minggu lalu.
Utas ini sangat membantu dalam menemukan pola berbeda yang menyebabkan masalah.

Terima kasih banyak untuk kalian semua. (Terutama mereka yang menyediakan kotak pasir. :-)


Saya tidak akan menanggapi semua orang secara pribadi secara rinci karena banyak kasus.

Sebagai gantinya, kami akan menulis resep umum dan gotcha dalam beberapa hari ke depan, dan menautkannya dari dokumen. Saya akan memastikan bahwa setiap pola umum di utas ini tercakup (atau meminta untuk menindaklanjuti dalam edisi terpisah untuk pola langka).

Setelah contoh diterbitkan, saya akan berkomentar di sini dan mengedit setiap komentar yang berisi CodeSandbox dengan menambahkan tautan ke jawaban/contoh yang relevan yang menunjukkan perbaikan yang tepat. Saya harap ini akan membantu Anda dan pembaca masa depan.

Bersulang!

❀️

@timkraut Anda harus dapat menambahkan fetchConfig pada deps, dan komponen harus membungkusnya dengan memo sehingga referensi tetap ada.
Contoh: https://codesandbox.io/s/9l015v2x4w


Masalah saya dengan ini adalah bahwa sekarang komponen perlu mengetahui tentang detail implementasi hook ...

Saya tidak yakin apakah utas ini masih terbuka untuk contoh diskusi, tetapi saya masih memikirkan praktik terbaik. Pertanyaan saya adalah tentang mengontrol kapan kait useEffect diaktifkan dengan larik dependensi dan menggunakan nilai-nilai pada waktu itu vs mengharuskan mendeklarasikan referensi terpisah untuk menyiasati aturan lint.

Contoh

https://codesandbox.io/s/40v54jnkyw

Dalam contoh ini, saya mencoba menyimpan nilai input secara otomatis secara berkala.

Penjelasan

Setiap 5 detik, kode mencoba menyimpan nilai saat ini secara otomatis (cetak saja ke layar untuk saat ini). Linter akan mengharuskan semua nilai input dimasukkan dalam larik dependensi yang akan mengubah seberapa sering efeknya akan diaktifkan.

  useEffect(
    () => {
      if (autoSaveTick % 5 === 0) {
        setAutosaveValue(
          `${input1Value.value}, ${input2Value.value}, ${input3Value.value}`
        );
      }
    },
    [autoSaveTick]
  );

Alternatif yang saya lihat adalah memiliki implementasi yang mirip dengan kait useTick dan useInterval ditentukan dalam contoh, di mana ada panggilan balik yang ditetapkan ke referensi. Sepertinya itu akan menyebabkan redefinisi fungsi yang tidak perlu hanya demi menyelaraskan dengan aturan lint.

Saya mencari praktik terbaik dalam menulis efek yang berjalan ketika satu variabel berubah (Variabel A) yang menggunakan nilai variabel lain (Variabel B & Variabel C) pada saat perubahan variabel sebelumnya (Variabel A).

Bagaimana jika fetchConfig berubah? Mengapa Anda berharap itu statis?

Saya akan baik-baik saja untuk menambahkannya ke array dependensi. Dalam kasus bisnis khusus kami, ini tidak akan pernah terjadi tetapi saya kira itu ide yang baik untuk menambahkannya.

Masalah saya dengan ini adalah bahwa sekarang komponen perlu mengetahui tentang detail implementasi hook ...

Itulah masalah yang kami miliki juga :/ Dalam implementasi kami, kami bahkan telah menggunakan properti tambahan untuk membuatnya lebih mudah untuk mengatur badan sehingga kami harus menggunakan 2 panggilan useMemo() setiap kali kami menggunakan hook ini. Belum yakin bagaimana mengatasi keterbatasan ini. Jika Anda punya ide, beri tahu saya!

Bagaimana Anda bisa menjalankan useEffect yang memiliki dependensi hanya sekali tanpa aturan mengeluh ketika melewati array kosong?

Katakan, sesuatu seperti:

useEffect(() => { init({ dsn, environment}) }, [])

Masalah saya dengan ini adalah bahwa sekarang komponen perlu mengetahui tentang detail implementasi hook ...

Saya tidak akan mengatakan ini adalah detail implementasi. Ini adalah API Anda. Jika suatu objek dilewatkan, kami menganggapnya dapat berubah kapan saja.

Jika dalam praktiknya selalu statis, Anda dapat menjadikannya argumen ke pabrik Hook. Seperti createFetch(config) yang mengembalikan useFetch() . Anda menelepon pabrik di tingkat atas.

Saya mengerti itu agak aneh. Kami memiliki masalah serupa dengan useSubscription yang sedang kami kerjakan. Tetapi karena ini adalah masalah umum, ini mungkin berarti useMemo sebenarnya adalah jawaban yang sah dan orang-orang harus terbiasa melakukannya untuk kasus ini.

Dalam praktiknya β€” kita akan melihat lebih banyak hal ini di masa mendatang. Tetapi Anda tidak boleh memperlakukan objek yang berpotensi dinamis sebagai objek statis karena pengguna tidak dapat mengubahnya.

@asylejmani Anda belum memberikan detail apa pun tentang kasus penggunaan Anda, seperti yang diminta di posting teratas. Mengapa Anda mengharapkan seseorang dapat memberikan jawaban atas pertanyaan Anda?

Inti dari aturan ini adalah untuk memberi tahu Anda bahwa environment dan dsn dapat berubah seiring waktu dan komponen Anda harus menanganinya. Jadi komponen Anda bermasalah (karena tidak menangani perubahan pada nilai-nilai itu) atau Anda memiliki beberapa kasus unik yang tidak masalah (dalam hal ini Anda harus menambahkan aturan serat, abaikan komentar yang menjelaskan alasannya).

Dalam kedua kasus itu tidak jelas apa yang Anda tanyakan. Aturan mengeluh bahwa segala sesuatunya bisa berubah, dan Anda tidak menangani perubahan itu. Tanpa contoh lengkap, Anda sendiri yang bisa menjawab mengapa menurut Anda penanganannya tidak perlu.

@gaearon maaf karena tidak lebih jelas (selalu terdengar jelas di kepala seseorang) :) pertanyaan saya lebih seperti bagaimana Anda mencapai componentDidMount dengan useEffect.

Saya mendapat kesan daripada array kosong melakukan itu, tetapi aturannya mengatakan kepada saya untuk selalu menyertakan dependensi dalam array.

@asylejmani Saya pikir gotcha terbesar dengan metode siklus hidup kelas seperti componentDidMount adalah bahwa kita cenderung menganggapnya sebagai metode yang terisolasi, tetapi sebenarnya itu adalah bagian dari aliran.
Jika Anda mereferensikan sesuatu di componentDidMount Anda kemungkinan besar perlu menanganinya di componentDidUpdate juga, atau komponen Anda mungkin bermasalah.
Inilah yang coba diperbaiki oleh aturan, Anda perlu menangani nilai dari waktu ke waktu.

  1. komponen terpasang, lakukan sesuatu dengan prop
  2. komponen diperbarui (misalnya: nilai prop telah berubah), lakukan sesuatu dengan nilai prop baru

Nomor 1 adalah tempat Anda meletakkan logika di componentDidMount / useEffect body
Nomor 2 adalah tempat Anda meletakkan logika di componentDidUpdate / useEffect deps

Aturannya adalah mengeluh Anda tidak melakukan bagian nomor 2 dari aliran

@gaearon maaf karena tidak lebih jelas (selalu terdengar jelas di kepala seseorang) :) pertanyaan saya lebih seperti bagaimana Anda mencapai componentDidMount dengan useEffect.

Saya mendapat kesan daripada array kosong melakukan itu, tetapi aturannya mengatakan kepada saya untuk selalu menyertakan dependensi dalam array.

Saya pikir saya dan @asylejmani berada di halaman yang sama di sini, tapi saya kira apa yang Anda katakan @gaearon adalah bahwa kita mungkin salah hanya menjalankan efek pada mount jika kita benar-benar memiliki dependensi.
Apakah itu pernyataan yang adil? Saya kira saya berpikir bahwa menyediakan array kosong seperti mengatakan "Saya tahu apa yang saya lakukan", tetapi saya mengerti mengapa Anda ingin tetap memiliki aturan.

Maaf karena belum menyediakan kotak pasir. Saya memulai malam itu dengan contoh Create React App, tidak dapat menemukan cara menjalankan eslint di kotak pasir, dan kemudian kehilangan kotak pasir saat memuat ulang browser tanpa menyimpan terlebih dahulu (dengan asumsi CodeSandbox temp disimpan, saya buruk).
Kemudian saya harus pergi tidur, dan tidak punya waktu sejak itu.

Bagaimanapun, saya mengerti apa yang Anda katakan, dan bahwa dalam skenario normal mungkin yang terbaik adalah memasukkan dependensi itu dan tidak menganggap itu cukup untuk dijalankan di mount saja.

Mungkin ada kasus penggunaan yang valid untuk itu juga, tetapi agak sulit untuk menjelaskan atau memberikan contoh yang baik, jadi saya akan hidup dengan menonaktifkan aturan sebaris saat diperlukan.

@asylejmani apakah kasus penggunaan Anda mirip dengan https://github.com/facebook/react/issues/14920#issuecomment -466378650? Saya tidak berpikir mungkin aturan untuk memahami skenario dalam kasus ini, jadi kita hanya perlu menonaktifkannya secara manual untuk jenis kode itu. Dalam semua kasus lain, aturan berfungsi sebagaimana mestinya.

Tidak yakin apakah ini masuk akal, tetapi satu skenario yang cukup umum bagi saya adalah seperti ini:

useEffect(() => {
    if(!dataIsLoaded) { // flag from redux
        loadMyData(); // redux action creator
    }
}, []);

Kedua dependensi ini berasal dari redux. Data (dalam hal ini) hanya boleh dimuat sekali, dan pembuat tindakan selalu sama.

Ini khusus untuk redux, dan aturan eslint Anda tidak dapat mengetahui ini, jadi saya mengerti mengapa itu harus memperingatkan. Masih bertanya-tanya apakah menyediakan array kosong mungkin hanya menonaktifkan aturan? Saya suka aturan itu memberi tahu saya tentang kehilangan deps jika saya memberikan beberapa tetapi tidak semua, atau jika saya tidak memberikannya sama sekali. Array kosong berarti sesuatu yang berbeda bagi saya. Tapi mungkin itu aku :)

Terima kasih untuk semua kerja keras Anda! Dan untuk membuat hidup kita sebagai pengembang lebih baik :)

Kasing penggunaan saya jauh lebih sederhana dan tentu saja saya dapat menambahkan semua dependensi dan itu akan tetap berfungsi sama, namun saya mendapat kesan bahwa aturan akan "memperingatkan" ketika Anda memiliki beberapa dependensi tetapi kehilangan yang lain.

Kasus penggunaan

Saya juga setuju bahwa dalam kasus ini menonaktifkan aturan sebaris adalah pilihan terbaik. Kasus itu Anda tahu persis apa yang Anda lakukan.

Saya percaya seluruh kebingungan saya adalah [] vs [beberapa], dan tentu saja terima kasih @gaearon untuk pekerjaan yang luar biasa :)

Saya pikir saya dan @asylejmani berada di halaman yang sama di sini, tapi saya kira apa yang Anda katakan @gaearon adalah bahwa kita mungkin salah hanya menjalankan efek pada mount jika kita benar-benar memiliki dependensi. Apakah itu pernyataan yang adil?

Ya. Jika komponen Anda tidak menangani pembaruan pada prop, biasanya itu bermasalah. Desain useEffect memaksa Anda untuk menghadapinya. Anda tentu saja dapat mengatasinya, tetapi standarnya adalah mendorong Anda untuk menangani kasus-kasus ini. Komentar ini menjelaskannya dengan baik: https://github.com/facebook/react/issues/14920#issuecomment -470913287.

Kedua dependensi ini berasal dari redux. Data (dalam hal ini) hanya boleh dimuat sekali, dan pembuat tindakan selalu sama.

Jika sama maka memasukkannya ke dalam dependensi tidak akan merugikan Anda. Saya ingin menekankannya β€” jika Anda yakin dependensi Anda tidak pernah berubah maka tidak ada salahnya mendaftarkannya . Namun, jika kemudian terjadi bahwa mereka berubah (misalnya jika komponen induk melewati fungsi yang berbeda tergantung pada status), komponen Anda akan menanganinya dengan benar.

Masih bertanya-tanya apakah menyediakan array kosong mungkin hanya menonaktifkan aturan?

Tidak. Menyediakan array kosong dan kemudian bertanya-tanya mengapa beberapa properti atau status basi adalah kesalahan yang paling umum.

>

Masuk akal, terima kasih

Pada 8 Mar 2019, pukul 15:27, Dan Abramov [email protected] menulis:

Saya pikir saya dan @asylejmani berada di halaman yang sama di sini, tapi saya kira apa yang Anda katakan @gaearon adalah bahwa kita mungkin salah hanya menjalankan efek pada mount jika kita benar-benar memiliki dependensi. Apakah itu pernyataan yang adil?

Ya. Jika komponen Anda tidak menangani pembaruan pada prop, biasanya itu bermasalah. Desain useEffect memaksa Anda untuk menghadapinya. Anda tentu saja dapat mengatasinya, tetapi standarnya adalah mendorong Anda untuk menangani kasus-kasus ini. Komentar ini menjelaskannya dengan baik: #14920 (komentar).

Kedua dependensi ini berasal dari redux. Data (dalam hal ini) hanya boleh dimuat sekali, dan pembuat tindakan selalu sama.

Jika sama maka memasukkannya ke dalam dependensi tidak akan merugikan Anda. Saya ingin menekankannya β€” jika Anda yakin dependensi Anda tidak pernah berubah maka tidak ada salahnya mendaftarkannya. Namun, jika kemudian terjadi bahwa mereka berubah (misalnya jika komponen induk melewati fungsi yang berbeda tergantung pada status), komponen Anda akan menanganinya dengan benar.

Masih bertanya-tanya apakah menyediakan array kosong mungkin hanya menonaktifkan aturan?

Tidak. Menyediakan array kosong dan kemudian bertanya-tanya mengapa beberapa properti atau status basi adalah kesalahan yang paling umum.

β€”
Anda menerima ini karena Anda disebutkan.
Balas email ini secara langsung, lihat di GitHub, atau matikan utasnya.

@lelah

Itu akan menyebabkan efek samping terjadi sebelum pembaruan aktual dilakukan, yang mungkin menyebabkan efek samping terpicu lebih sering daripada yang Anda inginkan.

Saya tidak yakin apa artinya ini. Bisakah Anda memberikan contoh?

Kami melewati ini dengan @threepointone hari ini. Berikut ringkasannya:

Diperbaiki dalam Aturan Lint

Ketergantungan useEffect asing

Aturan tidak mencegah Anda menambahkan deps "asing" ke useEffect lagi karena ada skenario yang sah.

Fungsi dalam komponen yang sama tetapi didefinisikan di luar efek

Linter tidak memperingatkan untuk kasus yang sekarang aman, tetapi dalam semua kasus lain ia memberi Anda saran yang lebih baik (seperti memindahkan fungsi di dalam efek, atau membungkusnya dengan useCallback ).

Layak Diperbaiki dalam Kode Pengguna

Menyetel ulang status pada perubahan alat peraga

Ini tidak menghasilkan pelanggaran serat lagi tetapi cara idiomatis untuk mengatur ulang status sebagai respons terhadap alat peraga berbeda . Solusi ini akan memiliki render ekstra tidak konsisten jadi saya tidak yakin itu diinginkan.

"Nilai non-fungsi saya konstan"

Kait mendorong Anda ke arah kebenaran sedapat mungkin. Jika Anda menentukan deps (yang dalam beberapa kasus Anda dapat menghilangkan), kami sangat menyarankan untuk menyertakan bahkan orang-orang yang Anda berpikir tidak akan berubah. Ya, dalam contoh useDebounce , penundaan tidak mungkin berubah. Tapi itu masih bug jika ya, tapi Hook tidak bisa mengatasinya. Ini juga muncul dalam skenario lain. (Misalnya Hooks jauh lebih kompatibel dengan hot reload karena setiap nilai diperlakukan sebagai dinamis.)

Jika Anda benar-benar bersikeras bahwa nilai tertentu statis, Anda dapat menerapkannya.
Cara teraman adalah melakukannya secara eksplisit di API Anda:

const useFetch = createFetch({ /* config object */});
const useDebounce = createDebounce(500);
const FormInput = createInput({ rules: [emailValidator, phoneValidator] });

Maka itu jelas tidak dapat berubah kecuali Anda memasukkannya ke dalam render. (Yang tidak akan menjadi penggunaan idiomatik dari Hook Anda.) Tetapi mengatakan bahwa <Slider min={50} /> tidak akan pernah dapat berubah tidak benar-benar valid β€” seseorang dapat dengan mudah mengubahnya menjadi <Slider min={state ? 50 : 100} /> . Bahkan, seseorang dapat melakukan ini:

let slider
if (isCelsius) {
  slider = <Slider min={0} max={100} />
} else {
  slider = <Slider min={32} max={212} />
}

Jika seseorang mengubah status isCelsius , komponen dengan asumsi min tidak pernah berubah akan gagal diperbarui. Tidak jelas dalam kasus ini bahwa Slider akan sama (tetapi itu karena memiliki posisi yang sama di pohon). Jadi ini adalah pijakan utama dalam hal membuat perubahan pada kode. Poin utama dari React adalah bahwa pembaruan dirender seperti status awal (Anda biasanya tidak dapat membedakan mana yang mana). Baik Anda merender nilai prop B, atau beralih dari nilai prop A ke B β€” itu akan terlihat dan berperilaku sama.

Meskipun ini tidak disarankan, dalam beberapa kasus, mekanisme penegakan bisa berupa Hook yang memperingatkan ketika nilai berubah (tetapi menyediakan yang pertama). Setidaknya itu lebih mungkin untuk diperhatikan.

function useMyHook(a) {
  const initialA = usePossiblyStaleValue(a);
  // ...
}

function usePossiblyStaleValue(value) {
  const ref = useRef(value);

  if (process.env.NODE_ENV !== 'production') {
    if (ref.current !== value) { // Or your custom comparison logic
      console.error(
        'Unlike normally in React, it is not supported ' +
        'to pass dynamic values to useMyHook(). Sorry!'
      );
    }
  }

  return ref.current;
}

Mungkin juga ada kasus yang sah di mana Anda tidak dapat menangani pembaruan. Seperti jika API tingkat bawah tidak mendukungnya, seperti plugin jQuery atau API DOM. Dalam hal ini peringatan masih diperlukan agar konsumen komponen Anda memahaminya. Atau, Anda dapat membuat komponen pembungkus yang menyetel ulang key pada pembaruan yang tidak kompatibel β€” memaksa remount bersih dengan props baru. Itu mungkin lebih baik untuk komponen daun seperti bilah geser atau kotak centang.

"Nilai fungsi saya konstan"

Pertama-tama, jika konstan dan diangkat ke lingkup tingkat atas maka linter tidak akan mengeluh. Tapi itu tidak membantu dengan hal-hal yang datang dari alat peraga atau konteks.

Jika itu benar-benar konstan maka menentukan dalam deps tidak sakit. Seperti kasus di mana fungsi setState di dalam Hook kustom dikembalikan ke komponen Anda, dan kemudian Anda memanggilnya dari sebuah efek. Aturan lint tidak cukup pintar untuk memahami tipuan seperti ini. Tetapi di sisi lain, siapa pun dapat membungkus panggilan balik itu nanti sebelum kembali, dan mungkin mereferensikan prop atau status lain di dalamnya. Maka itu tidak akan konstan! Dan jika Anda gagal menangani perubahan itu, Anda akan memiliki bug prop/state basi yang buruk. Jadi menentukannya adalah default yang lebih baik.

Namun, itu adalah kesalahpahaman bahwa nilai fungsi selalu konstan. Mereka lebih sering konstan di kelas karena pengikatan metode, meskipun itu menciptakan rentang bugnya sendiri . Tetapi secara umum, fungsi apa pun yang menutup nilai dalam komponen fungsi tidak dapat dianggap konstan. Aturan lint sekarang lebih cerdas dalam memberi tahu Anda apa yang harus dilakukan. (Seperti memindahkannya ke dalam efek β€” perbaikan paling sederhana β€” atau membungkusnya dengan useCallback .)

Ada masalah pada spektrum yang berlawanan dari ini, di mana Anda mendapatkan loop tak terbatas (nilai fungsi selalu berubah). Kami menangkapnya dalam aturan lint sekarang jika memungkinkan (dalam komponen yang sama) dan menyarankan perbaikan. Tapi itu rumit jika Anda melewati sesuatu beberapa tingkat ke bawah.

Anda masih dapat membungkusnya dengan useCallback untuk memperbaiki masalah. Ingat bahwa secara teknis itu valid untuk suatu fungsi untuk berubah , dan Anda tidak dapat mengabaikan kasus ini tanpa mempertaruhkan bug. Seperti onChange={shouldHandle ? handleChange : null} atau merender foo ? <Page fetch={fetchComments /> : <Page fetch={fetchArticles /> di tempat yang sama. Atau bahkan fetchComments yang menutup status komponen induk. Itu bisa berubah. Dengan kelas, perilakunya akan berubah secara diam-diam tetapi referensi fungsi akan tetap sama. Jadi anak Anda akan melewatkan pembaruan itu β€” Anda tidak benar-benar memiliki pilihan selain memberikan lebih banyak data kepada anak tersebut. Dengan komponen fungsi dan useCallback , identitas fungsi itu sendiri berubah β€” tetapi hanya jika diperlukan. Jadi itu adalah properti yang berguna dan bukan hanya penghalang yang harus dihindari.

Kita harus menambahkan solusi yang lebih baik untuk mendeteksi loop async tak terbatas. Itu harus mengurangi aspek yang paling membingungkan. Kami mungkin menambahkan beberapa deteksi untuk ini di masa mendatang. Anda juga dapat menulis sendiri sesuatu seperti ini:

useWarnAboutTooFrequentChanges([deps]);

Ini tidak ideal dan kita harus berpikir tentang menangani ini dengan lebih anggun. Saya setuju kasus seperti ini cukup buruk. Perbaikan tanpa melanggar aturan adalah membuat rules statis, misalnya dengan mengubah API menjadi createTextInput(rules) , dan membungkus register dan unregister menjadi useCallback . Lebih baik lagi, hapus register dan unregister , dan ganti dengan konteks terpisah di mana Anda meletakkan dispatch saja. Maka Anda dapat menjamin Anda tidak akan pernah memiliki identitas fungsi yang berbeda dari membacanya.

Saya akan menambahkan bahwa Anda mungkin ingin meletakkan useMemo pada nilai konteks karena penyedia melakukan banyak perhitungan yang akan menyedihkan untuk diulang jika tidak ada komponen baru yang terdaftar tetapi induknya sendiri diperbarui. Jadi ini menempatkan masalah yang mungkin tidak Anda sadari jika tidak lebih terlihat. Meskipun saya setuju kita perlu membuatnya lebih menonjol ketika ini terjadi.

Mengabaikan dependensi fungsi sepenuhnya mengarah ke bug yang lebih buruk dengan komponen fungsi dan Kait karena mereka akan terus melihat properti dan status basi jika Anda melakukannya. Jadi cobalah untuk tidak, ketika Anda bisa.

Bereaksi terhadap Perubahan Nilai Majemuk

Aneh bagi saya mengapa contoh ini menggunakan efek untuk sesuatu yang pada dasarnya adalah event handler. Melakukan "log" yang sama (saya berasumsi itu bisa berupa pengiriman formulir) di event handler tampaknya lebih pas. Ini terutama benar jika kita memikirkan apa yang terjadi ketika komponen dilepas. Bagaimana jika dilepas tepat setelah efek dijadwalkan? Hal-hal seperti pengiriman formulir seharusnya tidak hanya "tidak terjadi" dalam kasus itu. Jadi sepertinya efek mungkin pilihan yang salah di sana.

Yang mengatakan Anda masih dapat melakukan apa yang Anda coba β€” dengan membuat fullName menjadi setSubmittedData({firstName, lastName}) sebagai gantinya, dan kemudian [submittedData] adalah ketergantungan Anda, dari mana Anda dapat membaca firstName dan lastName .

Mengintegrasikan dengan Imperatif/Kode Warisan

Saat berintegrasi dengan hal-hal penting seperti plugin jQuery atau API DOM mentah, beberapa hal buruk mungkin diharapkan. Yang mengatakan saya masih mengharapkan Anda untuk dapat mengkonsolidasikan efek sedikit lebih dalam contoh itu.


Semoga saya tidak melupakan siapa pun! Beri tahu saya jika saya melakukannya atau jika ada sesuatu yang tidak jelas. Kami akan segera mencoba mengubah pelajaran dari ini menjadi beberapa dokumen.

@gaearon , terima kasih telah meluangkan waktu untuk menyelami masalah dan meringkas item tindakan ke dalam kategori yang berbeda. Anda melewatkan sampel saya (#14920 (komentar) oleh @trevorgithub) dalam komentar ringkasan penutup Anda. (Saya tentu menghargai ada banyak umpan balik dari banyak orang; Saya pikir komentar asli saya hilang di bagian item tersembunyi di suatu tempat di tengah-tengah komentar masalah).

Saya berasumsi sampel saya akan termasuk dalam 'Mengintegrasikan dengan Imperatif/Kode Warisan', meskipun mungkin juga kategori lain?

Dalam kasus 'Mengintegrasikan dengan Imperatif/Kode Warisan', sepertinya tidak banyak yang bisa dilakukan. Dalam kasus tersebut, bagaimana seseorang mengabaikan peringatan ini? Sepertinya:
// eslint-disable-line react-hooks/exhaustive-deps

Maaf saya melewatkan yang satu ini.

Jika Anda menerima beberapa data melalui alat peraga tetapi tidak ingin menggunakan alat peraga itu sampai beberapa perubahan eksplisit, kedengarannya seperti cara yang benar untuk memodelkannya adalah dengan memiliki status turunan.

Anda memikirkannya sebagai "Saya ingin mengabaikan perubahan ke penyangga sampai penyangga lain". Tetapi Anda juga dapat menganggapnya sebagai β€œKomponen saya memiliki fungsi pengambilan di negara bagian. Ini diperbarui dari prop ketika prop lain berubah. ”

Status turunan umumnya tidak disarankan tetapi di sini sepertinya yang Anda inginkan. Cara paling sederhana untuk mengimplementasikan ini adalah seperti:

const [currentGetData, setCurrentGetData] = useState(getData);
const [prevRefreshRequest, setPrevRefreshRequest] = useState(refreshRequest);

if (prevRefreshRequest !== refreshRequest) {
  setPrevRefreshRequest(refreshRequest);
  setCurrentGetData(getData);
}

useEffect(() => {
  currentGetData(someId);
}, [currentGetData, someId]);

Anda juga harus meletakkan useCallback sekitar getData yang Anda wariskan.

Perhatikan bahwa secara umum pola meneruskan fungsi async untuk mengambil tampaknya teduh bagi saya. Saya kira dalam kasus Anda, Anda menggunakan Redux sehingga masuk akal. Tetapi jika fungsi async didefinisikan dalam komponen induk, itu akan mencurigakan karena Anda mungkin memiliki kondisi balapan. Efek Anda tidak memiliki pembersihan jadi bagaimana Anda bisa tahu kapan pengguna memilih ID yang berbeda? Ada risiko permintaan yang datang tidak sesuai pesanan dan pengaturan status yang salah. Jadi itu hanya sesuatu yang perlu diingat. Jika pengambilan data ada di dalam komponen itu sendiri, maka Anda dapat memiliki fungsi pembersihan efek yang menetapkan tanda "abaikan" untuk mencegah setState dari respons. (Tentu saja memindahkan data ke cache eksternal sering kali merupakan solusi yang lebih baik β€” begitulah cara kerja Suspense juga.)

Melakukan "log" yang sama (saya berasumsi itu bisa berupa pengiriman formulir) di event handler tampaknya lebih pas.

@gaearon masalah yang saya lihat adalah melakukannya di event handler berarti efek samping terjadi sebelum pembaruan dilakukan. Tidak ada jaminan ketat bahwa komponen akan berhasil dirender ulang sebagai hasil dari event tersebut, jadi melakukannya di event handler bisa jadi prematur.

Misalnya, jika saya ingin mencatat bahwa pengguna telah berhasil mengirimkan permintaan pencarian baru dan melihat hasilnya. Jika terjadi kesalahan dan komponen terlempar, saya tidak ingin peristiwa log itu terjadi.

Lalu ada kasus di mana efek samping ini mungkin async sehingga menggunakan useEffect memberi Anda fungsi pembersihan.

Ada juga masalah useReducer , di mana nilai yang mungkin ingin saya log tidak akan tersedia di event handler. Tapi saya pikir itu sudah ada di radar kalian

Dalam kedua kasus tersebut, pendekatan yang Anda rekomendasikan kemungkinan sudah cukup. Simpan status komposisi dalam bentuk di mana Anda masih dapat mengakses nilai individual yang dibuatnya.

Saya memiliki kait praktis untuk membungkus fungsi dengan parameter tambahan dan meneruskannya. Ini terlihat seperti ini:

function useBoundCallback(fn, ...bound) {
  return useCallback((...args) => fn(...bound, ...args), [fn, ...bound]);
}

(sebenarnya sedikit lebih rumit karena memiliki beberapa fitur tambahan tetapi di atas masih menunjukkan masalah yang relevan).

Kasus penggunaan adalah ketika komponen perlu meneruskan properti ke anak, dan ingin anak memiliki cara untuk memodifikasi properti spesifik itu. Misalnya, pertimbangkan daftar di mana setiap item memiliki opsi edit. Objek induk meneruskan nilai ke setiap anak, dan fungsi panggilan balik untuk dipanggil jika ingin mengedit nilai. Anak tidak tahu ID item mana yang ditampilkan, jadi orang tua harus mengubah panggilan balik untuk menyertakan parameter ini. Ini bisa disarangkan ke kedalaman yang sewenang-wenang.

Contoh sederhana dari alur ini ditampilkan di sini: https://codesandbox.io/s/vvv36834k5 (mengklik "Pergi!" menunjukkan log konsol yang menyertakan jalur komponen)


Masalahnya adalah saya mendapatkan 2 kesalahan linter dengan aturan ini:

React Hook (X) memiliki ketergantungan yang hilang: 'terikat'. Sertakan atau hapus larik ketergantungan

React Hook (X) memiliki elemen spread dalam larik ketergantungannya. Ini berarti kami tidak dapat memverifikasi secara statis apakah Anda telah melewati dependensi yang benar

Mengubahnya untuk menggunakan daftar parameter (yaitu tidak menggunakan operator spread) akan menghancurkan memoisasi, karena daftar dibuat dengan setiap pemanggilan meskipun parameternya identik.


Pikiran:

  • apakah layak menunjukkan kesalahan pertama ketika yang kedua juga berlaku?
  • apakah ada cara saya dapat menonaktifkan aturan ini khusus untuk tempat-tempat di mana operator spread digunakan?
  • jika satu-satunya penggunaan variabel di dalam suatu fungsi adalah dengan operator spread, aman untuk menggunakan operator spread di dependensi, dan karena aturan sudah mendeteksi ini, tentu saja itu harus mengizinkannya. Saya menyadari ada kasus yang lebih kompleks yang lebih sulit untuk diselesaikan dengan analisis statis, tetapi ini sepertinya merupakan kemenangan mudah untuk penggunaan spread yang relatif umum.

Hai @gaearon , saya baru saja mendapat peringatan ini, yang belum saya temukan dibahas di mana pun:

Accessing 'myRef.current' during the effect cleanup will likely read a different ref value because by this time React has already updated the ref. If this ref is managed by React, store 'myRef.current' in a variable inside the effect itself and refer to that variable from the cleanup function.

Saya menemukan pesan ini agak membingungkan. Saya kira itu mencoba memperingatkan saya bahwa nilai arus ref saat pembersihan dapat berbeda dari nilai pada badan efek. Benar?
Jika itu masalahnya, dan menyadarinya, apakah aman/sah untuk mengabaikan peringatan ini?

Kasus saya, jika menarik: CodeSandbox
Konteks: beberapa kait pengambilan data khusus. Saya menggunakan penghitung dalam referensi untuk mencegah kondisi balapan dan pembaruan pada komponen yang tidak dipasang.

Saya pikir saya bisa menghindari peringatan ini dengan menyembunyikan referensi yang dibaca di dalam suatu fungsi, atau membuat referensi boolean lain untuk kasus pembersihan. Tapi saya merasa tidak perlu bertele-tele jika saya bisa mengabaikan peringatan ini.

@lelah

Misalnya, jika saya ingin mencatat bahwa pengguna telah berhasil mengirimkan permintaan pencarian baru dan melihat hasilnya. Jika terjadi kesalahan dan komponen terlempar, saya tidak ingin peristiwa log itu terjadi.

Ya itu terdengar seperti kasus penggunaan khusus. Saya pikir ketika kebanyakan orang menginginkan "efek samping" yang mereka maksudkan adalah pengiriman formulir itu sendiri β€” bukan fakta bahwa Anda melihat formulir yang dikirimkan. Dalam hal ini solusi yang saya berikan tampaknya baik-baik saja.

@davidje13

apakah layak menunjukkan kesalahan pertama ketika yang kedua juga berlaku?

Harap ajukan masalah baru untuk proposal untuk mengubah aturan lint.

apakah ada cara saya dapat menonaktifkan aturan ini khusus untuk tempat-tempat di mana operator spread digunakan?

Anda selalu dapat // eslint-disable-next-line react-hooks/exhaustive-deps jika Anda merasa tahu apa yang Anda lakukan.

jika satu-satunya penggunaan variabel di dalam suatu fungsi adalah dengan operator spread, aman untuk menggunakan operator spread di dependensi, dan karena aturan sudah mendeteksi ini, tentu saja itu harus mengizinkannya.

Ajukan masalah baru pls.

@CarlosGines

Saya menemukan pesan ini agak membingungkan. Saya kira itu mencoba memperingatkan saya bahwa nilai arus ref saat pembersihan dapat berbeda dari nilai pada badan efek. Benar?

Ya.

Jika itu masalahnya, dan menyadarinya, apakah aman/sah untuk mengabaikan peringatan ini?

Ummm .. tidak jika itu mengarah ke bug. πŸ™‚

Konteks: beberapa kait pengambilan data khusus. Saya menggunakan penghitung dalam referensi untuk mencegah kondisi balapan dan pembaruan pada komponen yang tidak dipasang.

Ya mungkin kasus penggunaan ini sah. Ajukan masalah baru untuk didiskusikan pls?

Saya akan mengunci masalah ini karena kami mendapat cukup umpan balik dan telah dimasukkan.

Pertanyaan & jawaban umum: https://github.com/facebook/react/issues/14920#issuecomment -471070149

Jika Anda ingin menyelam lebih dalam tentang useEffect dan dependensi, ada di sini: https://overreacted.io/a-complete-guide-to-useeffect/

Kami juga akan segera menambahkan lebih banyak hal ke dokumen.

Jika Anda ingin mengubah sesuatu dalam aturan atau tidak yakin kasus Anda sah, ajukan masalah baru.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat