Panduan UseEffect Lengkap

Anda telah menulis beberapa komponen menggunakan kait . Mungkin - mereka bahkan membuat aplikasi kecil. Secara umum, Anda cukup senang dengan hasilnya. Anda terbiasa dengan API dan dalam prosesnya menemukan beberapa trik berguna yang tidak jelas. Anda bahkan membuat beberapa kait Anda sendiri dan mengurangi kode Anda menjadi 300 baris, menempatkan di dalamnya apa yang sebelumnya diwakili oleh pengulangan fragmen program. Apa yang Anda lakukan, Anda menunjukkan kepada rekan kerja. "Bagus sekali," kata mereka tentang proyek Anda.


Tetapi kadang-kadang, ketika Anda menggunakan useEffect , komponen-komponen mekanisme perangkat lunak tidak cocok bersama dengan sangat baik. Tampaknya bagi Anda bahwa Anda kehilangan sesuatu. Semua ini mirip dengan bekerja dengan acara siklus hidup komponen berbasis kelas ... tapi benarkah demikian?
Mencoba memahami apa yang sebenarnya tidak cocok untuk Anda, Anda perhatikan bahwa Anda mengajukan pertanyaan berikut:

  • Bagaimana cara memainkan componentDidMount menggunakan useEffect ?
  • Bagaimana cara memuat data di dalam useEffect ? Apa itu [] ?
  • Apakah fungsi perlu ditentukan sebagai dependensi efek?
  • Mengapa suatu program kadang-kadang berakhir dalam loop tanpa akhir dari memuat ulang data?
  • Mengapa kondisi lama terkadang terlihat di dalam efek atau properti lama ditemukan?

Ketika saya pertama kali mulai menggunakan kait, pertanyaan-pertanyaan ini juga menyiksa saya. Bahkan ketika saya sedang mempersiapkan dokumentasinya, saya tidak dapat mengatakan bahwa saya benar-benar menguasai beberapa seluk-beluknya. Sejak itu, saya memiliki beberapa momen ketika, tiba-tiba, menyadari sesuatu yang penting, saya benar-benar ingin berseru: "Eureka!" Tentang apa yang saya sadari pada saat-saat ini, saya ingin memberi tahu Anda. Apa yang Anda pelajari tentang useEffect sekarang akan memungkinkan Anda untuk melihat dengan jelas jawaban yang jelas untuk pertanyaan di atas.

Tetapi untuk melihat jawaban atas pertanyaan-pertanyaan ini, pertama-tama kita harus mengambil langkah mundur. Tujuan artikel ini bukan untuk memberi para pembacanya beberapa petunjuk langkah demi langkah untuk bekerja dengan useEffect . Hal ini bertujuan membantu Anda, sehingga useEffect " useEffect " useEffect . Dan terus terang, tidak banyak yang bisa dipelajari. Bahkan, sebagian besar waktu kita akan dihabiskan untuk melupakan apa yang kita ketahui sebelumnya.

Semua yang ada di kepala saya menyatu hanya setelah saya berhenti melihat kaitan useEffect melalui prisma metode siklus siklus komponen berbasis komponen.

"Anda harus melupakan apa yang telah diajarkan kepada Anda"


habr.com/ru/company/ruvds/blog/445276/Yoda


Diasumsikan bahwa pembaca materi ini agak akrab dengan useEffect API. Ini adalah artikel yang agak panjang, bisa dibandingkan dengan buku kecil. Faktanya adalah saya lebih suka mengekspresikan pikiran saya dengan cara ini. Di bawah ini, sangat singkat, jawaban diberikan untuk pertanyaan-pertanyaan yang dibahas di atas. Mungkin mereka bermanfaat bagi mereka yang tidak punya waktu atau keinginan untuk membaca semua materi.

Jika format di mana kami akan mempertimbangkan useEffect , dengan semua penjelasan dan contohnya, sangat tidak cocok untuk Anda, Anda dapat menunggu sedikit - sampai saat ketika penjelasan ini muncul dalam manual lain yang tak terhitung jumlahnya. Ini adalah kisah yang sama dengan perpustakaan Bereaksi itu sendiri, yang pada 2013 adalah sesuatu yang sama sekali baru. Butuh waktu bagi komunitas pengembangan untuk mengenali model mental baru dan untuk materi pendidikan berdasarkan model ini muncul.

Jawaban atas pertanyaan


Berikut adalah jawaban singkat untuk pertanyaan yang diajukan di awal materi ini, ditujukan bagi mereka yang tidak ingin membaca seluruh teks ini. Jika, ketika membaca jawaban-jawaban ini, Anda merasa bahwa Anda tidak benar-benar memahami makna dari apa yang Anda baca, perhatikan materi yang ada. Anda akan menemukan penjelasan terperinci dalam teks. Jika Anda akan membaca semuanya, Anda dapat melewati bagian ini.

▍Cara memainkan componentDidMount menggunakan useEffect?


Meskipun Anda dapat menggunakan useEffect(fn, []) untuk memutar fungsionalitas componentDidMount , itu tidak sama persis dengan componentDidMount . Yaitu, itu, tidak seperti componentDidMount , menangkap properti dan status. Karena itu, bahkan di dalam callback, Anda akan melihat properti dan status awal. Jika Anda ingin melihat versi terbaru dari sesuatu, Anda dapat menuliskannya di tautan ref . Tetapi biasanya ada cara yang lebih sederhana untuk menyusun kode, jadi melakukan ini adalah opsional. Ingat bahwa model efek mental berbeda dari yang berlaku untuk componentDidMount dan metode siklus hidup komponen lainnya. Oleh karena itu, mencoba menemukan padanan yang tepat dapat lebih membahayakan daripada kebaikan. Agar dapat bekerja secara produktif, Anda perlu, untuk berbicara, untuk "berpikir dalam efek." Dasar dari model mental mereka lebih dekat ke implementasi sinkronisasi daripada untuk menanggapi peristiwa dalam siklus hidup komponen.

OwCara memuat data dengan benar di dalam useEffect? Apa itu []?


Berikut adalah panduan yang baik tentang memuat data menggunakan useEffect . Cobalah untuk membacanya secara keseluruhan! Tidak sebesar itu. Tanda kurung, [] , mewakili array kosong, berarti bahwa efeknya tidak menggunakan nilai-nilai yang terlibat dalam aliran data Bereaksi, dan karena alasan ini penggunaan tunggal dapat dianggap aman. Selain itu, penggunaan array dependensi yang kosong adalah sumber kesalahan yang umum jika nilai tertentu sebenarnya digunakan dalam efek. Anda perlu menguasai beberapa strategi (terutama disajikan dalam bentuk useReducer dan useCallback ) yang dapat membantu menghilangkan kebutuhan untuk ketergantungan daripada membuang ketergantungan ini secara tidak masuk akal.

▍ Apakah fungsi perlu ditentukan sebagai efek dependensi?


Disarankan bahwa fungsi yang tidak memerlukan properti atau keadaan diambil di luar komponen, dan fungsi-fungsi yang hanya digunakan oleh efek direkomendasikan untuk ditempatkan di dalam efek. Jika setelah itu efek Anda masih menggunakan fungsi-fungsi yang berada dalam ruang lingkup render (termasuk fungsi dari properti), bungkus dengan useCallback di mana mereka dideklarasikan, dan cobalah untuk menggunakannya lagi. Mengapa ini penting? Fungsi dapat “melihat” nilai dari properti dan status, sehingga mereka mengambil bagian dalam aliran data. Berikut ini informasi lebih rinci tentang ini di FAQ kami.

▍ Mengapa suatu program kadang-kadang berakhir dalam satu lingkaran reload data yang tak berujung?


Ini bisa terjadi ketika memuat data dilakukan dalam efek yang tidak memiliki argumen kedua yang mewakili dependensi. Tanpa itu, efek dilakukan setelah setiap operasi rendering - yang berarti bahwa pengaturan negara akan menyebabkan efek tersebut ditarik kembali. Infinite loop juga dapat terjadi jika nilai yang selalu berubah ditunjukkan dalam array dependensi. Cari tahu nilainya seperti apa dengan menghapus dependensi satu per satu. Namun, menghapus dependensi (atau menggunakan [] dengan terburu-buru) biasanya merupakan pendekatan yang salah untuk menyelesaikan masalah. Sebagai gantinya, Anda harus menemukan sumber masalah dan benar-benar menyelesaikannya. Misalnya, fungsi dapat menyebabkan masalah serupa. Anda dapat membantu menyelesaikannya dengan meletakkannya di efek, memindahkannya di luar komponen, atau dengan membungkus useCallback . Untuk menghindari membuat beberapa objek, Anda bisa menggunakan useMemo .

▍ Mengapa terkadang kondisi lama terlihat di dalam efek atau properti lama ditemukan?


Efek selalu "melihat" properti dan status dari render di mana mereka dinyatakan. Ini membantu mencegah kesalahan , tetapi dalam beberapa kasus dapat mengganggu pengoperasian komponen secara normal. Dalam kasus seperti itu, Anda dapat menggunakan tautan ref dapat ref secara eksplisit untuk bekerja dengan nilai-nilai tersebut (Anda dapat membaca tentang ini di akhir artikel yang disebutkan sebelumnya). Jika Anda berpikir bahwa Anda melihat properti atau keadaan dari render lama, tetapi jangan berharap ini, maka Anda mungkin telah melewatkan beberapa dependensi. Untuk belajar melihatnya, gunakan aturan linter ini. Dalam beberapa hari, itu akan menjadi seperti sifat kedua Anda. Juga, lihat jawaban ini di FAQ kami.

Saya harap jawaban atas pertanyaan-pertanyaan ini bermanfaat bagi mereka yang membacanya. Sekarang mari kita bicara lebih banyak tentang useEffect .

Setiap render memiliki sifat dan statusnya sendiri.


Sebelum kita dapat membahas efeknya, kita perlu berbicara tentang rendering.

Berikut adalah komponen penghitung fungsional.

 function Counter() { const [count, setCount] = useState(0); return (   <div>     <p>You clicked {count} times</p>     <button onClick={() => setCount(count + 1)}>       Click me     </button>   </div> ); } 

Lihatlah dari dekat garis <p>You clicked {count} times</p> . Apa yang dia maksud Apakah konstanta konstan entah bagaimana "mengamati" perubahan di negara dan apakah itu diperbarui secara otomatis? Kesimpulan ini dapat dianggap semacam ide pertama yang berharga dari seseorang yang mempelajari Bereaksi, tetapi itu bukan model mental yang akurat tentang apa yang terjadi.

Dalam contoh kita, count hanyalah angka. Ini bukan semacam "ikatan data" ajaib, bukan semacam "objek pengamat" atau "proxy", atau apa pun. Sebelum kita adalah nomor lama yang baik, seperti ini:

 const count = 42; // ... <p>You clicked {count} times</p> // ... 

Selama output komponen pertama, nilai count diperoleh dari useState() adalah 0. Ketika kita memanggil setCount(1) , React memanggil komponen itu lagi. count waktu ini adalah 1. Dan seterusnya:

 //     function Counter() { const count = 0; //  useState() // ... <p>You clicked {count} times</p> // ... } //       function Counter() { const count = 1; //  useState() // ... <p>You clicked {count} times</p> // ... } //        function Counter() { const count = 2; //  useState() // ... <p>You clicked {count} times</p> // ... } 

Bereaksi memanggil komponen setiap kali kita memperbarui negara. Akibatnya, setiap operasi rendering “melihat” nilainya sendiri dari status counter , yang, di dalam fungsi, adalah konstan.

Akibatnya, baris ini tidak melakukan operasi pengikatan data khusus:

 <p>You clicked {count} times</p> 

Ini hanya menyematkan nilai numerik dalam kode yang dihasilkan selama rendering. Nomor ini disediakan oleh React. Ketika kita memanggil setCount , React memanggil komponen lagi dengan nilai count berbeda. Bereaksi kemudian memperbarui DOM sehingga model objek dokumen cocok dengan output data terbaru selama rendering komponen.

Kesimpulan paling penting yang dapat ditarik dari ini adalah bahwa count adalah konstan di dalam render tertentu dan tidak berubah seiring waktu. Komponen yang dipanggil berulang kali berubah. Setiap render "melihat" nilai count sendiri, yang diisolasi untuk setiap operasi rendering.

Dalam materi ini Anda dapat menemukan detail tentang proses ini.

Setiap render memiliki penangan acara sendiri.


Semuanya masih jelas. Bagaimana dengan penangan event?
Lihatlah contoh ini. Di sini, tiga detik setelah mengklik tombol, kotak pesan ditampilkan dengan informasi tentang nilai yang disimpan dalam count :

 function Counter() { const [count, setCount] = useState(0); function handleAlertClick() {   setTimeout(() => {     alert('You clicked on: ' + count);   }, 3000); } return (   <div>     <p>You clicked {count} times</p>     <button onClick={() => setCount(count + 1)}>       Click me     </button>     <button onClick={handleAlertClick}>       Show alert     </button>   </div> ); } 

Misalkan saya melakukan urutan tindakan berikut:

  • Saya akan membawa nilai count ke 3 dengan mengklik tombol Click me .
  • Klik pada tombol Show alert .
  • Naikkan nilainya menjadi 5 sebelum batas waktu berakhir.


Meningkatkan nilai hitungan setelah mengklik tombol Perlihatkan tanda

Menurut Anda, apa yang muncul di kotak pesan? Apakah 5 akan ditampilkan di sana, yang sesuai dengan nilai count pada saat timer dipicu, atau 3 - yaitu, nilai count pada saat tombol ditekan?

Sekarang Anda akan menemukan jawaban untuk pertanyaan ini, tetapi jika Anda ingin mengetahui semuanya sendiri - ini adalah versi yang berfungsi dari contoh ini.

Jika apa yang Anda lihat tampaknya tidak dapat dipahami oleh Anda - ini adalah contoh yang lebih dekat dengan kenyataan. Bayangkan aplikasi obrolan di mana, dalam keadaan, ID penerima pesan saat ini disimpan, dan ada tombol Send . Dalam materi ini , apa yang terjadi dipertimbangkan secara rinci. Bahkan, jawaban yang benar untuk pertanyaan tentang apa yang muncul di kotak pesan adalah 3.

Mekanisme untuk menampilkan kotak pesan "menangkap" keadaan pada saat tombol diklik.

Ada beberapa cara untuk mengimplementasikan versi perilaku lainnya, tetapi untuk sekarang kita akan membahas perilaku standar sistem. Ketika membangun model mental teknologi, penting untuk membedakan "jalan perlawanan paling tidak" dari semua jenis "pintu keluar darurat".

Bagaimana cara kerjanya?

Kami telah mengatakan bahwa nilai count adalah konstan untuk setiap panggilan khusus ke fungsi kami. Saya pikir layak untuk membahas hal ini secara lebih rinci. Intinya adalah bahwa fungsi kita dipanggil berkali-kali (satu kali untuk setiap operasi rendering), tetapi dengan masing-masing panggilan ini, count di dalamnya adalah konstan. Konstanta ini diset ke nilai tertentu (mewakili keadaan operasi rendering tertentu).

Perilaku fungsi ini bukan sesuatu yang istimewa untuk Bereaksi - fungsi biasa berperilaku dengan cara yang sama:

 function sayHi(person) { const name = person.name; setTimeout(() => {   alert('Hello, ' + name); }, 3000); } let someone = {name: 'Dan'}; sayHi(someone); someone = {name: 'Yuzhi'}; sayHi(someone); someone = {name: 'Dominic'}; sayHi(someone); 

Dalam contoh ini , variabel eksternal someone dipindahkan beberapa kali. Hal yang sama dapat terjadi di suatu tempat di dalam Bereaksi, keadaan komponen saat ini dapat berubah. Namun, di dalam fungsi sayHi ada name konstanta lokal yang dikaitkan dengan person dari panggilan tertentu. Konstanta ini bersifat lokal, oleh karena itu nilainya dalam panggilan fungsi yang berbeda diisolasi satu sama lain! Akibatnya, setelah batas waktu, setiap jendela pesan yang ditampilkan "mengingat" nilai name sendiri.

Ini menjelaskan bagaimana event handler kami menangkap nilai count ketika sebuah tombol diklik. Jika kita, bekerja dengan komponen, menerapkan prinsip yang sama, ternyata setiap render “melihat” nilai count sendiri:

 //     function Counter() { const count = 0; //  useState() // ... function handleAlertClick() {   setTimeout(() => {     alert('You clicked on: ' + count);   }, 3000); } // ... } //       function Counter() { const count = 1; //  useState() // ... function handleAlertClick() {   setTimeout(() => {     alert('You clicked on: ' + count);   }, 3000); } // ... } //        function Counter() { const count = 2; //  useState() // ... function handleAlertClick() {   setTimeout(() => {     alert('You clicked on: ' + count);   }, 3000); } // ... } 

Akibatnya, setiap render, pada kenyataannya, mengembalikan "versi" sendiri handleAlertClick . Masing-masing versi ini "mengingat" nilai count sendiri:

 //     function Counter() { // ... function handleAlertClick() {   setTimeout(() => {     alert('You clicked on: ' + 0);   }, 3000); } // ... <button onClick={handleAlertClick} /> // ,   0 // ... } //       function Counter() { // ... function handleAlertClick() {   setTimeout(() => {     alert('You clicked on: ' + 1);   }, 3000); } // ... <button onClick={handleAlertClick} /> // ,   1 // ... } //        function Counter() { // ... function handleAlertClick() {   setTimeout(() => {     alert('You clicked on: ' + 2);   }, 3000); } // ... <button onClick={handleAlertClick} /> // ,   2 // ... } 

Itulah sebabnya dalam contoh ini , penangan acara "milik" render tertentu, dan ketika Anda mengklik tombol, komponen menggunakan status count dari render ini.

Dalam setiap render tertentu, properti dan status selalu tetap sama. Tetapi jika operasi rendering yang berbeda menggunakan properti dan status mereka sendiri, hal yang sama terjadi dengan mekanisme apa pun yang menggunakannya (termasuk event handler). Mereka juga "milik" render tertentu. Oleh karena itu, bahkan fungsi asinkron di dalam event handler akan "melihat" nilai count sama.

Perlu dicatat bahwa dalam contoh di atas, saya menyematkan nilai count spesifik langsung ke fungsi handleAlertClick . Penggantian "mental" ini tidak akan merugikan kita, karena count konstan tidak dapat diubah dalam render tertentu. Pertama, itu adalah konstanta, dan kedua, itu angka. Aman untuk mengatakan bahwa seseorang juga dapat berpikir tentang makna lain, seperti objek, tetapi hanya jika kita menerima sebagai aturan untuk tidak membuat perubahan (mutasi) di negara bagian. Pada saat yang sama, kami puas dengan panggilan ke setSomething(newObj) dengan objek baru alih-alih mengubah yang sudah ada, karena dengan pendekatan ini keadaan milik render sebelumnya tidak tersentuh.

Setiap render memiliki efek sendiri.


Materi ini, seperti yang Anda tahu, dikhususkan untuk efek, tetapi kami belum membicarakannya. Sekarang kita akan memperbaikinya. Ternyata, bekerja dengan efek tidak jauh berbeda dari apa yang telah kita ketahui.

Pertimbangkan contoh dari dokumentasi, yang sangat mirip dengan yang telah kita analisis:

 function Counter() { const [count, setCount] = useState(0); useEffect(() => {   document.title = `You clicked ${count} times`; }); return (   <div>     <p>You clicked {count} times</p>     <button onClick={() => setCount(count + 1)}>       Click me     </button>   </div> ); } 

Sekarang saya punya pertanyaan untuk Anda. Bagaimana efek membaca nilai count terbaru?

Mungkin beberapa "pengikatan data" digunakan di sini, atau "objek pengamat" yang memperbarui nilai count di dalam fungsi efek? Mungkin count adalah variabel yang dapat berubah yang nilainya React set di dalam komponen kami, akibatnya efeknya selalu melihat versi terbarunya?

Tidak.

Kita sudah tahu bahwa dalam rendering komponen tertentu, count adalah konstan. Bahkan event handler “melihat” nilai count dari render tempat mereka “milik” karena fakta bahwa count adalah konstanta yang terletak dalam cakupan tertentu. Hal yang sama berlaku untuk efek!

Dan harus dicatat bahwa ini bukan jumlah variabel count yang entah bagaimana berubah di dalam efek "tidak berubah". Di depan kami adalah fungsi efek itu sendiri, yang berbeda di setiap operasi rendering.

Setiap versi "melihat" nilai count dari render yang menjadi "miliknya":

 //     function Counter() { // ... useEffect(   //        () => {     document.title = `You clicked ${0} times`;   } ); // ... } //       function Counter() { // ... useEffect(   //        () => {     document.title = `You clicked ${1} times`;   } ); // ... } //        function Counter() { // ... useEffect(   //        () => {     document.title = `You clicked ${2} times`;   } ); // .. } 

React mengingat fungsi efek yang kami sediakan, menjalankannya setelah mengatur ulang nilai-nilai di DOM, dan memungkinkan peramban untuk menampilkan gambar.

Akibatnya, bahkan jika kita berbicara di sini tentang satu-satunya efek konseptual (memperbarui judul dokumen), itu diwakili dalam setiap render dengan fungsi baru, dan setiap fungsi efek "melihat" properti dan negara dari render khusus yang menjadi "milik".

Efeknya, secara konseptual, dapat direpresentasikan sebagai bagian dari hasil rendering.

Sebenarnya, ini tidak demikian (untuk memungkinkan komposisi kait ). , , , .

, , :

React:

  • , 0.

:

  • : <p>You clicked 0 times</p> .
  • , , : () => { document.title = 'You clicked 0 times' } .

React:

  • . . , , - DOM.

:

  • , .

React:

  • , , .
  • () => { document.title = 'You clicked 0 times' } .

, . , , - :

:

  • , React, 1.

React:

  • , 1.

:

  • : <p>You clicked 1 times</p> .
  • , , : () => { document.title = 'You clicked 1 times' } .

React:

  • . . , , - DOM.

:

  • , .

React:

  • , , .
  • () => { document.title = 'You clicked 1 times' } .


, , , , «» .

. :

 function Counter() { const [count, setCount] = useState(0); useEffect(() => {   setTimeout(() => {     console.log(`You clicked ${count} times`);   }, 3000); }); return (   <div>     <p>You clicked {count} times</p>     <button onClick={() => setCount(count + 1)}>       Click me     </button>   </div> ); } 

, ?

, . , , , . ! , , , , , count . .




: «, ! ?».

, , this.setState , , . , , , , , :

   componentDidUpdate() {   setTimeout(() => {     console.log(`You clicked ${this.state.count} times`);   }, 3000); } 

, this.state.count count , , . , , , 5 , 5 .




, JavaScript-, , , , , setTimeout , . , (React this.state , ), .

— , , «» , . , , , . , , . , , , , , , .


, ( , , - API ) , .

:

 function Example(props) { useEffect(() => {   setTimeout(() => {     console.log(props.counter);   }, 1000); }); // ... } function Example(props) { const counter = props.counter; useEffect(() => {   setTimeout(() => {     console.log(counter);   }, 1000); }); // ... } 

, «» . ! . , .

, , - , , , , . , ref , .

, , , , , . , ( ), «» React-. , , . , .

, , , :

 function Example() { const [count, setCount] = useState(0); const latestCount = useRef(count); useEffect(() => {   //        count   latestCount.current = count;   setTimeout(() => {     //            console.log(`You clicked ${latestCount.current} times`);   }, 3000); }); // ... 




- React . React this.state . , , latestCount.current . , . , , , .

?


, . , , «» .

:

 useEffect(() => {   ChatAPI.subscribeToFriendStatus(props.id, handleStatusChange);   return () => {     ChatAPI.unsubscribeFromFriendStatus(props.id, handleStatusChange);   }; }); 

, props{id: 10} , {id: 20} — . , :

  • React {id: 10} .
  • React {id: 20} .
  • React {id: 20} .

( , , .)

, «» - , , «» - , . — , , , . .

React , . , . . . :

  • React {id: 20} .
  • . {id: 20} .
  • React {id: 10} .
  • React {id: 20} .

, «» props , {id: 10} , , props {id: 20} .

, …


— ?

: « ( , , - API ) , ».

! « » , . , , :

 //  ,  props  {id: 10} function Example() { // ... useEffect(   //       () => {     ChatAPI.subscribeToFriendStatus(10, handleStatusChange);     //           return () => {       ChatAPI.unsubscribeFromFriendStatus(10, handleStatusChange);     };   } ); // ... } //  ,  props  {id: 20} function Example() { // ... useEffect(   //       () => {     ChatAPI.subscribeToFriendStatus(20, handleStatusChange);     //           return () => {       ChatAPI.unsubscribeFromFriendStatus(20, handleStatusChange);     };   } ); // ... } 

, , … , «» , -, {id: 10} .

React . , , . props , .

,


React , . .

, :

 function Greeting({ name }) { return (   <h1 className="Greeting">     Hello, {name}   </h1> ); } 

, <Greeting name="Dan" /> , — <Greeting name="Yuzhi" /> , <Greeting name="Yuzhi" /> . Hello, Yuzhi .

, , . React, . , , . $.addClass $.removeClass jQuery- ( — , «»), , CSS- React ( — , «»).

React DOM , . «» «».

. useEffect , React, .

 function Greeting({ name }) { useEffect(() => {   document.title = 'Hello, ' + name; }); return (   <h1 className="Greeting">     Hello, {name}   </h1> ); } 

useEffect , , . , - , ! , «», «».

, A , B , — C , , C . (, - ), .

, , , . ( ).

?

React


React DOM. DOM , React DOM, - .

, :

 <h1 className="Greeting"> Hello, Dan </h1> 

:

 <h1 className="Greeting"> Hello, Yuzhi </h1> 

React :

 const oldProps = {className: 'Greeting', children: 'Hello, Dan'}; const newProps = {className: 'Greeting', children: 'Hello, Yuzhi'}; 

React , children , DOM. , className . :

 domNode.innerText = 'Hello, Yuzhi'; // domNode.className    

- ? , , .

, , - :

 function Greeting({ name }) { const [counter, setCounter] = useState(0); useEffect(() => {   document.title = 'Hello, ' + name; }); return (   <h1 className="Greeting">     Hello, {name}     <button onClick={() => setCounter(counter + 1)}>       Increment     </button>   </h1> ); } 

counter . document.title name , name . document.title counter , .

React … ?

 let oldEffect = () => { document.title = 'Hello, Dan'; }; let newEffect = () => { document.title = 'Hello, Dan'; }; //   React  ,        ? 

— . React , , . ( . name .)

, , ( deps ), useEffect :

   useEffect(() => {   document.title = 'Hello, ' + name; }, [name]); //   

, React: «, , , , name ».

, , React :

 const oldEffect = () => { document.title = 'Hello, Dan'; }; const oldDeps = ['Dan']; const newEffect = () => { document.title = 'Hello, Dan'; }; const newDeps = ['Dan']; // React     ,     . //      ,     . 

, , ! - - .

React


React — . , , , , useEffect , , , . ( !)

 function SearchResults() { async function fetchData() {   // ... } useEffect(() => {   fetchData(); }, []); //   ?  .      . // ... } 

FAQ , . .

« !», — . : , , . , , , — , .

, , . , , , , . , . .

, , .

, React


, , React , .

   useEffect(() => {   document.title = 'Hello, ' + name; }, [name]); 




, , , [] , , , , :

   useEffect(() => {   document.title = 'Hello, ' + name; }, []); // :    name 




. , «» , , .

, , , . , : « setInterval clearInterval ». . , , , useEffect , , , [] . - , ?

 function Counter() { const [count, setCount] = useState(0); useEffect(() => {   const id = setInterval(() => {     setCount(count + 1);   }, 1000);   return () => clearInterval(id); }, []); return <h1>{count}</h1>; } 

, , .

, « , », . , , , setInterval , . , ?

, — React , , . , count , React , , , . — .

count 0. setCount(count + 1) setCount(0 + 1) . , — [] , setCount(0 + 1) :

 //  ,   0 function Counter() { // ... useEffect(   //       () => {     const id = setInterval(() => {       setCount(0 + 1); //  setCount(1)     }, 1000);     return () => clearInterval(id);   },   [] //    ); // ... } //       1 function Counter() { // ... useEffect(   //     - ,    //   React  ,   .   () => {     const id = setInterval(() => {       setCount(1 + 1);     }, 1000);     return () => clearInterval(id);   },   [] ); // ... } 

React, , , — .

count — , ( ):

   const count = // ... useEffect(() => {   const id = setInterval(() => {     setCount(count + 1);   }, 1000);   return () => clearInterval(id); }, []); 

. React .


,

. , , React , , . — - .


React , . , , , .

, , , . count :

 useEffect(() => { const id = setInterval(() => {   setCount(count + 1); }, 1000); return () => clearInterval(id); }, [count]); 

. , , — , . count , count , setCount(count + 1) :

 //  ,   0 function Counter() { // ... useEffect(   //       () => {     const id = setInterval(() => {       setCount(0 + 1); // setCount(count + 1)     }, 1000);     return () => clearInterval(id);   },   [0] // [count] ); // ... } //  ,   1 function Counter() { // ... useEffect(   //       () => {     const id = setInterval(() => {       setCount(1 + 1); // setCount(count + 1)     }, 1000);     return () => clearInterval(id);   },   [1] // [count] ); // ... } 

, setInterval , count , . , .


,

, , , . — , .

.


, count .

 useEffect(() => {   const id = setInterval(() => {     setCount(count + 1);   }, 1000);   return () => clearInterval(id); }, [count]); 

, , count . , count setCount . , , count . , , setState :

   useEffect(() => {   const id = setInterval(() => {     setCount(c => c + 1);   }, 1000);   return () => clearInterval(id); }, []); 

« ». , count - , setCount(count + 1) . count - , count + 1 «» React. React count . , React — , , , .

setCount(c => c + 1) . « React », , . « » , , .

, , , . React. count :


,

.

, setInterval , , c => c + 1 . count . React .

Google Docs


, , — ? , , «», , . , Google Docs . . , .

, . . , setCount(c => c + 1) , , setCount(count + 1) , «» count . , ( — «»). « React» — . .

( ) , Google Docs . — , React . , , ( , , ) .

, setCount(c => c + 1) , . , . , , , , , . setCount(c => c + 1) . useReducer .


, : count step . setInterval , step :

 function Counter() { const [count, setCount] = useState(0); const [step, setStep] = useState(1); useEffect(() => {   const id = setInterval(() => {     setCount(c => c + step);   }, 1000);   return () => clearInterval(id); }, [step]); return (   <>     <h1>{count}</h1>     <input value={step} onChange={e => setStep(Number(e.target.value))} />   </> ); } 

.

, React . step , . .

: step setIntervalstep . , , , ! , , , , , .

, , , setInterval , step . step ?

, , useReducer .

, setSomething(something => ...) , , . «», , , .

step dispatch :

 const [state, dispatch] = useReducer(reducer, initialState); const { count, step } = state; useEffect(() => { const id = setInterval(() => {   dispatch({ type: 'tick' }); //  setCount(c => c + step); }, 1000); return () => clearInterval(id); }, [dispatch]); 

.

: « , ?». , React , dispatch . .

!

( dispatch setstate useRef , React , . — .)

, , , , . step . , . , . :

 const initialState = { count: 0, step: 1, }; function reducer(state, action) { const { count, step } = state; if (action.type === 'tick') {   return { count: count + step, step }; } else if (action.type === 'step') {   return { count, step: action.step }; } else {   throw new Error(); } } 

, , , .

useReducer — -


, , , . , , ? , , API <Counter step={1} /> . , props.step ?

, ! , :

 function Counter({ step }) { const [count, dispatch] = useReducer(reducer, 0); function reducer(state, action) {   if (action.type === 'tick') {     return state + step;   } else {     throw new Error();   } } useEffect(() => {   const id = setInterval(() => {     dispatch({ type: 'tick' });   }, 1000);   return () => clearInterval(id); }, [dispatch]); return <h1>{count}</h1>; } 

, . , , , , . .

dispatch . , , . .

, , . «» , , ? , dispatch , React . . .

useReducer «-» . , . , , , , .


, - , .

, , , :

 function SearchResults() { const [data, setData] = useState({ hits: [] }); async function fetchData() {   const result = await axios(     'https://hn.algolia.com/api/v1/search?query=react',   );   setData(result.data); } useEffect(() => {   fetchData(); }, []); //   ? // ... 

, .

, , . , , , , , , , , .

, , , , :

 function SearchResults() { // ,       function getFetchUrl() {   return 'https://hn.algolia.com/api/v1/search?query=react'; } // ,        async function fetchData() {   const result = await axios(getFetchUrl());   setData(result.data); } useEffect(() => {   fetchData(); }, []); // ... } 

, , :

 function SearchResults() { const [query, setQuery] = useState('react'); // ,       function getFetchUrl() {   return 'https://hn.algolia.com/api/v1/search?query=' + query; } // ,        async function fetchData() {   const result = await axios(getFetchUrl());   setData(result.data); } useEffect(() => {   fetchData(); }, []); // ... } 

, (, ), . .

, . , :

 function SearchResults() { // ... useEffect(() => {   //      !   function getFetchUrl() {     return 'https://hn.algolia.com/api/v1/search?query=react';   }   async function fetchData() {     const result = await axios(getFetchUrl());     setData(result.data);   }   fetchData(); }, []); //    . // ... } 

.

? , « ». React, - .

getFetchUrl , query , , , , . — , query :

 function SearchResults() { const [query, setQuery] = useState('react'); useEffect(() => {   function getFetchUrl() {     return 'https://hn.algolia.com/api/v1/search?query=' + query;   }   async function fetchData() {     const result = await axios(getFetchUrl());     setData(result.data);   }   fetchData(); }, [query]); //    . // ... } 

.

, « React». query . , , , , . , , .

exhaustive-deps eslint-plugin-react-hooks , . , , .




.

, ?


. , , . , , .

? , . : React . . , « ». , , . , , , !

, , . , getFetchUrl :

 function SearchResults() { function getFetchUrl(query) {   return 'https://hn.algolia.com/api/v1/search?query=' + query; } useEffect(() => {   const url = getFetchUrl('react');   // ...    -   ... }, []); //  : getFetchUrl useEffect(() => {   const url = getFetchUrl('redux');   // ...    -   ... }, []); //  : getFetchUrl // ... } 

getFetchUrl — , .

, «» , . getFetchUrl (, , ), :

 function SearchResults() { //       function getFetchUrl(query) {   return 'https://hn.algolia.com/api/v1/search?query=' + query; } useEffect(() => {   const url = getFetchUrl('react');   // ...    -    ... }, [getFetchUrl]); //   ,     . useEffect(() => {   const url = getFetchUrl('redux');   // ...    -   ... }, [getFetchUrl]); //   ,     . // ... } 

, getFetchUrl . , — . - , , . , , , .

— .

, , :

 //        function getFetchUrl(query) { return 'https://hn.algolia.com/api/v1/search?query=' + query; } function SearchResults() { useEffect(() => {   const url = getFetchUrl('react');   // ...    -   ... }, []); //     . useEffect(() => {   const url = getFetchUrl('redux');   // ...    -   ... }, []); //     . // ... } 

, . , , .

. , useCallback :

 function SearchResults() { //    ,   const getFetchUrl = useCallback((query) => {   return 'https://hn.algolia.com/api/v1/search?query=' + query; }, []);  //      . useEffect(() => {   const url = getFetchUrl('react');   // ...    -   ... }, [getFetchUrl]); //      . useEffect(() => {   const url = getFetchUrl('redux');   // ...    -   ... }, [getFetchUrl]); //       // ... } 

useCallback . : , -, , , .

, . ( 'react' 'redux' ). , , , query . , , query , getFetchUrl .

, query useCallback :

 function SearchResults() { const [query, setQuery] = useState('react'); const getFetchUrl = useCallback(() => { //   query   return 'https://hn.algolia.com/api/v1/search?query=' + query; }, []); //  : query // ... } 

useCallback query , , getFetchUrl , query :

 function SearchResults() { const [query, setQuery] = useState('react'); //      query const getFetchUrl = useCallback(() => {   return 'https://hn.algolia.com/api/v1/search?query=' + query; }, [query]);  //    . useEffect(() => {   const url = getFetchUrl();   // ...    -   ... }, [getFetchUrl]); //    . // ... } 

useCallback , query , getFetchUrl , , . query , getFetchUrl , . Excel: - , , , .

— , . , :

 function Parent() { const [query, setQuery] = useState('react'); //      query const fetchData = useCallback(() => {   const url = 'https://hn.algolia.com/api/v1/search?query=' + query;   // ...      ... }, [query]);  //       return <Child fetchData={fetchData} /> } function Child({ fetchData }) { let [data, setData] = useState(null); useEffect(() => {   fetchData().then(setData); }, [fetchData]); //       // ... } 

fetchData Parent query , Child , .

?


, , , , . , , , , :

 class Parent extends Component { state = {   query: 'react' }; fetchData = () => {   const url = 'https://hn.algolia.com/api/v1/search?query=' + this.state.query;   // ...    -   ... }; render() {   return <Child fetchData={this.fetchData} />; } } class Child extends Component { state = {   data: null }; componentDidMount() {   this.props.fetchData(); } render() {   // ... } } 

, : « , , , useEffectcomponentDidMount componentDidUpdate . !». componentDidUpdate :

 class Child extends Component { state = {   data: null }; componentDidMount() {   this.props.fetchData(); } componentDidUpdate(prevProps) {   //         if (this.props.fetchData !== prevProps.fetchData) {     this.props.fetchData();   } } render() {   // ... } } 

, fetchData — ! (, , , .) - , . this.props.fetchData prevProps.fetchData . , , ?

   componentDidUpdate(prevProps) {   this.props.fetchData(); } 

. . ( .) , fetchData this.state.query ?

   render() {   return <Child fetchData={this.fetchData.bind(this, this.state.query)} />; } 

this.props.fetchData !== prevProps.fetchData true , , query ! .

, , , query Child . , , query , query :

 class Parent extends Component { state = {   query: 'react' }; fetchData = () => {   const url = 'https://hn.algolia.com/api/v1/search?query=' + this.state.query;   // ...    -    ... }; render() {   return <Child fetchData={this.fetchData} query={this.state.query} />; } } class Child extends Component { state = {   data: null }; componentDidMount() {   this.props.fetchData(); } componentDidUpdate(prevProps) {   if (this.props.query !== prevProps.query) {     this.props.fetchData();   } } render() {   // ... } } 

, , - , , .

, , . this , . , , , , - . , this.props.fetchData , , , , , .

- useCallback . , , , . , . useCallback props.fetchData .

, useMemo :

 function ColorPicker() { //         Child, //       . const [color, setColor] = useState('pink'); const style = useMemo(() => ({ color }), [color]); return <Child style={style} />; } 

, useCallback , - . « », , , . , . , .

, fetchData ( ), . , , . (« props.onComplete , ?») , .


, :

 class Article extends Component { state = {   article: null }; componentDidMount() {   this.fetchData(this.props.id); } async fetchData(id) {   const article = await API.fetchArticle(id);   this.setState({ article }); } // ... } 

, , , . . — , :

 class Article extends Component { state = {   article: null }; componentDidMount() {   this.fetchData(this.props.id); } componentDidUpdate(prevProps) {   if (prevProps.id !== this.props.id) {     this.fetchData(this.props.id);   } } async fetchData(id) {   const article = await API.fetchArticle(id);   this.setState({ article }); } // ... } 

, , , . , . , {id: 10} , {id: 20} , , . , , , . .

, , . — , , async/await ( , - ) , ( , ).

, async -. (, , , , .)

, , ! .

, :

 function Article({ id }) { const [article, setArticle] = useState(null); useEffect(() => {   let didCancel = false;   async function fetchData() {     const article = await API.fetchArticle(id);     if (!didCancel) {       setArticle(article);     }   }   fetchData();   return () => {     didCancel = true;   }; }, [id]); // ... } 

, , , . , .


, , , , , . , , , . . — .

useEffect , , , . React. , useEffect .

, , « », . . , , , , «» , .

, useEffect , . — . — , , — , . , , , , API.

, , useFetch , , useTheme , . , , useEffect . , , , .

, , useEffect . — , . , . ?

Suspense React , , - ( : , , ) .

Suspense , , useEffect , , , - . , , , . , , , , .

Ringkasan


, . , , - , , , .

Source: https://habr.com/ru/post/id445276/


All Articles