React ingin komponen Anda dapat diprediksi. Berikan state dan props yang sama, dan ia harus menampilkan UI yang sama setiap saat. Namun, sebagian besar aplikasi nyata tidak dapat bertahan di dalam gelembung tersebut. Mereka perlu menjangkau dunia luar. Sebuah dashboard membutuhkan angka terbaru dari server. Sebuah widget chat perlu mendengarkan pesan. Sebuah timer perlu berdetak. Operasi-operasi ini adalah side effect, dan mereka berada di luar siklus render React. Hook useEffect adalah tempat Anda menempatkan pekerjaan yang berantakan dan tidak terprediksi tersebut agar komponen Anda sendiri tetap konsisten.
Side Effects: Apa yang Seharusnya Berada di Dalam useEffect
Side effect adalah apa pun yang menyentuh dunia di luar pengembalian JSX. Fase render React haruslah murni (pure). Saat Anda mulai mengambil data, menulis ke variabel global, atau memasang listener ke DOM, Anda telah melangkah keluar dari wilayah murni tersebut.
Contoh umum meliputi:
- Mengambil data dari API
- Menyiapkan timer atau interval
- Menambahkan event listener ke
windowataudocument - Memperbarui judul tab browser
- Menghubungkan ke WebSockets
Tugas-tugas ini memiliki satu kesamaan: mereka tidak seharusnya berada di dalam pernyataan return komponen Anda atau selama logika render utama. Mencoba memanggil API browser seperti setInterval secara langsung di dalam body render akan memicu fungsi tersebut pada setiap render, sehingga menciptakan timer duplikat dan perilaku yang membingungkan. useEffect ada justru untuk mengisolasi pekerjaan ini dan menjalankannya pada saat yang tepat.
Bagaimana Dependency Array Mengontrol Waktu
Argumen kedua untuk useEffect adalah dependency array, dan ini adalah sumber kebingungan terbesar bagi pengembang yang berpindah dari class component. Anggaplah ini sebagai sekumpulan variabel yang dipantau oleh React untuk memutuskan apakah akan melewati atau menjalankan effect Anda setelah render saat ini.
Ada tiga pola yang akan Anda gunakan berulang kali.
Tanpa dependency array sama sekali. Jika Anda mengabaikan array tersebut sepenuhnya, React berasumsi Anda ingin effect berjalan setelah setiap render, termasuk yang pertama. Ini jarang sekali menjadi apa yang Anda butuhkan. Jika effect Anda melakukan permintaan jaringan atau operasi DOM yang berat, menjalankannya pada setiap ketukan tombol atau perubahan state akan merusak performa. Gunakan pola ini hanya jika Anda benar-benar perlu menjalankan ulang sesuatu karena prop atau state apa pun mungkin telah berubah dan Anda tidak dapat menentukan mana yang berubah.
Array kosong []. Ini memberi tahu React untuk menjalankan effect satu kali, segera setelah komponen terpasang (mount) dan DOM siap. Ini adalah tempat yang tepat untuk pengambilan data awal. Misalnya, jika komponen Anda memuat data profil pengguna, Anda ingin permintaan tersebut dilakukan tepat satu kali saat halaman profil muncul, bukan setiap kali pengguna berinteraksi dengan formulir di bagian bawah halaman.
Array dengan variabel tertentu [count]. Ini adalah alat presisi. React membandingkan nilai saat ini dari dependensi ini dengan nilainya selama render terakhir. Jika salah satu dari mereka berubah, effect akan berjalan. Jika tidak ada yang berubah dalam daftar tersebut, React akan melewati effect sepenuhnya.
Jika Anda menyinkronkan judul tab browser dengan variabel state, Anda harus memasukkan variabel tersebut ke dalam dependency array. React kemudian akan memperbarui judul hanya saat nilai tersebut berubah. Jika Anda mengabaikannya, judul akan tetap usang (stale). Jika Anda memasukkan variabel state yang tidak terkait, Anda membuang-buang siklus untuk memperbarui judul karena perubahan yang tidak penting.
Cleanup Tidak Boleh Dilewatkan
Beberapa effect meninggalkan jejak. Sebuah timer terus berhitung. Sebuah event listener terus memicu fungsi. Sebuah WebSocket tetap terbuka. Saat komponen Anda unmount, atau bahkan saat sebuah effect dijalankan ulang karena dependensinya berubah, React tidak secara otomatis membersihkan residu dari effect sebelumnya. Itu adalah tugas Anda.
Anda membuat fungsi cleanup dengan mengembalikan (return) sebuah fungsi dari dalam useEffect. React memanggil cleanup ini sebelum menerapkan effect berikutnya, dan sekali lagi saat komponen meninggalkan layar.
Anda harus menggunakan cleanup untuk:
- Menghapus interval atau timeout dengan
clearIntervalatauclearTimeout - Menghapus event listener yang ditambahkan ke
window,document, atau node eksternal - Berhenti berlangganan (unsubscribing) dari aliran data atau layanan
Abaikan hal ini, dan Anda akan mendapatkan kebocoran memori (memory leaks). Sebuah komponen terpasang (mount), memasang listener scroll, lalu unmount, namun listener tersebut tetap ada. Browser tetap menyimpan callback dan node DOM yang dirujuknya. Seiring waktu, terutama dalam aplikasi single-page dengan navigasi yang intens, "hantu-hantu" ini akan menumpuk dan memperlambat tab. Solusinya biasanya hanya beberapa baris kode: kembalikan sebuah fungsi yang menghapus apa yang telah Anda tambahkan.
Kesalahan Umum yang Terbawa ke Produksi
Bahkan pengembang berpengalaman pun sering menggunakan useEffect padahal ada opsi yang lebih sederhana. Berikut adalah tiga pola yang harus menjadi tanda peringatan saat peninjauan kode.
Loop tak terbatas. Jangan pernah memperbarui variabel state di dalam useEffect jika variabel yang sama berada di dalam dependency array Anda, kecuali jika Anda memiliki kondisi gerbang (gate condition) yang memutus siklus tersebut. Jika Anda membaca count, menambah nilainya, dan mencantumkan count sebagai dependency, React akan mendeteksi perubahan tersebut, melakukan re-render, menjalankan effect lagi, menambah nilainya lagi, dan akhirnya membuat browser macet.
Effect yang tidak perlu. Jangan gunakan useEffect untuk menghitung nilai dari props atau state yang sudah ada. Jika Anda dapat menurunkannya secara langsung saat render, lakukan saja. Nilai turunan (derived values) seharusnya berada di dalam body komponen atau dalam kalkulasi yang di-memoize dengan useMemo. Memindahkannya ke dalam sebuah effect akan memecah logika Anda ke dalam fase render dan fase effect tanpa memberikan manfaat apa pun dan membuat kode lebih sulit diikuti.
Alat yang salah untuk tindakan pengguna. `use
