useEffect menjalankan sepotong kode setelah React merender komponenmu dan memperbarui layar. Kamu memakainya untuk menjaga komponen tetap sinkron dengan sesuatu di luar React, seperti timer, koneksi jaringan, atau event listener, dan kamu mengatur kapan kode itu berjalan lagi dengan array dependensi.
Buka console di bawah pratinjau: effect mencatat log sekali saat komponen muncul, lalu lagi setelah setiap klik. Ganti [count] menjadi [] lalu klik lagi: effect tetap diam setelah log pertama.
Sintaksnya
useEffect(() => {
// setup: runs after the render is on the screen
return () => {
// cleanup: runs before the next setup, and on unmount
};
}, [dependencies]);
- Argumen pertama adalah fungsi setup. Fungsi ini bisa mengembalikan fungsi cleanup, atau tidak mengembalikan apa pun.
- Argumen kedua adalah array dependensi: setiap nilai dari komponen yang dibaca setup (props, state, dan apa pun yang dihitung dari keduanya).
- Panggil
useEffectdi tingkat teratas komponen, seperti setiap hook.
Effect berjalan setelah render
Rendering adalah saat React memanggil fungsi komponenmu untuk mencari tahu apa yang harus tampil di layar. Effect menunggu sampai pekerjaan itu selesai dan DOM diperbarui, jadi di dalam effect halaman sudah menampilkan nilai-nilai yang baru.
Setiap klik mencatat render lebih dulu dan effect kemudian, dan effect membaca teks tombol yang baru saja ditulis React. Inilah sebabnya membaca DOM, memulai timer, atau menyambung ke server tempatnya di effect, bukan di badan komponen: badan komponen berjalan saat rendering, sebelum ada apa pun di layar, dan React mungkin memanggilnya lebih sering dari yang kamu kira.
Effect biasanya berjalan setelah browser menggambar layar (paint). Saat sebuah effect harus mengukur DOM sebelum paint, pakai useLayoutEffect, yang selalu berjalan sebelum paint.
Array dependensi: tiga bentuk
Array dependensi menentukan kapan effect berjalan lagi. Ada tiga bentuk, dan blok ini memakai ketiganya agar kamu bisa melihat perbedaannya.
Saat dimuat, ketiga effect mencatat log. Klik a + 1 dan dua di antaranya berjalan: yang tanpa array dan yang bergantung pada a. Klik b + 1 dan hanya yang tanpa array yang berjalan, karena tidak ada yang berubah di [] atau [a].
| Array dependensi | Kapan effect berjalan |
|---|---|
| tidak ada | Setelah setiap render |
[] | Sekali, setelah render pertama (saat mount) |
[a, b] | Setelah render pertama, lalu setiap kali a atau b berubah |
React membandingkan setiap dependensi dengan nilai sebelumnya memakai Object.is. Angka, string, dan boolean dibandingkan berdasarkan nilai. Object, array, dan fungsi dibandingkan berdasarkan identitas, dan ini sangat penting (lihat di bawah).
Jangan menghilangkan sebuah nilai dari array agar effect lebih jarang berjalan. Effect itu akan terus memakai nilai dari render lama. Aturan lint react-hooks/exhaustive-deps menangkap hal ini; aturan itu bagian dari setup ESLint yang disertakan template React dari Vite dan konfigurasi ESLint dari Next.js.
Cleanup
Banyak effect memulai sesuatu yang harus dihentikan: koneksi, subscription, timer. Kembalikan sebuah fungsi dari effect, dan React memanggilnya di dua saat: tepat sebelum effect berjalan lagi dengan dependensi baru, dan saat komponen meninggalkan layar (unmount).
Pilih room lain: console menampilkan disconnect from general lalu connect to music. Koneksi lama selalu ditutup sebelum koneksi baru dibuka. Klik Hide dan cleanup berjalan untuk terakhir kalinya, karena ChatRoom sudah di-unmount.
Fungsi connect di sini menggantikan client chat atau WebSocket sungguhan. Bentuknya sama untuk semuanya: buat di setup, tutup di cleanup.
Menghentikan timer
Timer yang dimulai di effect akan terus berjalan setelah komponennya hilang, kecuali cleanup menghentikannya.
Hapus baris clearInterval(id);, lalu sembunyikan timer-nya: tick terus berdatangan di console dari komponen yang sudah tidak ada. Setiap Show memulai timer yang lain lagi.
Perhatikan setSeconds((s) => s + 1). Effect berjalan sekali, jadi setSeconds(seconds + 1) biasa akan selamanya membaca seconds dari render pertama dan berhenti di 1. Fungsi updater selalu mendapat nilai terbaru.
Menghapus event listener
Event listener di window atau document mengikuti pola yang sama. Berikan fungsi yang sama ke removeEventListener dengan yang kamu berikan ke addEventListener:
useEffect(() => {
function handleKeyDown(e) {
if (e.key === 'Escape') onClose();
}
window.addEventListener('keydown', handleKeyDown);
return () => window.removeEventListener('keydown', handleKeyDown);
}, [onClose]);
Perulangan tanpa akhir
Effect yang mengatur nilai state yang juga menjadi dependensinya memulai sebuah perulangan: state berubah, React merender, dependensinya berubah, effect berjalan dan mengatur state itu lagi.
// Never stops: every render runs the effect, every effect causes a render
useEffect(() => {
setCount(count + 1);
});
// Same loop: count changes, so the effect runs again
useEffect(() => {
setCount(count + 1);
}, [count]);
Komponen dirender tanpa henti dan tab browser terus sibuk. React tidak menghentikan perulangan ini: di development React mencatat error "Maximum update depth exceeded" ke console, dan di production tidak mencatat apa pun. Blok ini menjalankan versi kedua dengan kondisi berhenti, jadi kamu bisa melihat perulangannya tanpa membuat halaman macet:
Console mencatat render 0 sampai render 5: enam render untuk satu mount, masing-masing dimulai oleh effect. Perbaikannya bergantung pada maksudmu:
- Kamu ingin effect berjalan sekali. Pakai
[]dan bentuk updater,setCount((c) => c + 1), sehinggacountbukan dependensi. - Kamu menghitung sebuah nilai dari state lain. Jangan simpan nilai itu sama sekali; hitung saat render, seperti yang ditunjukkan di bagian terakhir halaman ini.
- Kamu bereaksi terhadap aksi pengguna. Taruh kodenya di event handler.
Object dan fungsi sebagai dependensi
Object, array, atau fungsi yang dibuat di badan komponen adalah nilai baru di setiap render, jadi effect yang bergantung padanya berjalan setelah setiap render, bahkan saat tidak ada isinya yang berubah.
Mengganti warna tidak ada hubungannya dengan pencarian, tapi setiap toggle mencatat search for react lagi. Pindahkan baris options ke dalam effect dan ganti array-nya menjadi [query]: toggle menjadi sepi dan hanya mengetik di input yang menjalankan pencarian.
Itulah perbaikan umumnya. Buat object dan fungsi di dalam effect yang memakainya, dan bergantunglah pada nilai primitif yang menyusunnya. Saat sebuah fungsi harus berada di luar effect, useCallback menjaga identitasnya tetap stabil di antara render.
Mengapa effect berjalan dua kali di development
Dengan <StrictMode> aktif (aplikasi Vite baru membungkus root dengannya, dan App Router di Next.js mengaktifkannya secara default), React menjalankan sebuah tes di development: React me-mount setiap komponen, menjalankan effect-nya, menjalankan cleanup-nya, lalu menjalankan effect itu lagi. Untuk chat room di atas, console development menampilkan:
connect to general
disconnect from general
connect to general
Ini terjadi sekali, saat mount, dan hanya di development. Tujuannya untuk membongkar effect yang tidak punya cleanup: kalau dua kali jalan berturut-turut merusak sesuatu, pengguna juga akan merusaknya dengan pergi lalu kembali. Perbaikannya bukan memblokir jalan kedua dengan ref; perbaikannya adalah menulis cleanup. Pratinjau di halaman ini berjalan seperti build production, jadi kamu tidak akan melihat jalan ganda di sini.
Mungkin kamu tidak butuh effect
Effect dipakai untuk sinkronisasi dengan sistem di luar React. Dua kegunaan yang umum lebih baik tanpa effect.
Nilai yang dihitung dari props atau state. Hitung saat render. Menyimpannya di state dan menyinkronkannya dengan effect memakan satu render tambahan, dan render pertama dari keduanya menaruh nilai yang sudah usang di DOM:
Setiap klik merender WithEffect dua kali, dan render pertamanya masih menampilkan nama lama. Computed dirender sekali dengan nilai yang benar.
Kode yang merespons aksi pengguna. Mengirim formulir, menampilkan toast setelah pembelian, atau mengirim event analytics untuk sebuah klik tempatnya di event handler. Di sana kamu tahu persis apa yang terjadi, sedangkan effect hanya tahu bahwa ada nilai yang berubah.
useEffectEvent
Kadang sebuah effect butuh nilai terbaru dari sebuah prop tanpa berjalan ulang saat prop itu berubah. Di chat room, notifikasi harus memakai theme yang sekarang, tapi mengganti tema tidak boleh menyambung ulang koneksi. React 19.2 menambahkan useEffectEvent untuk ini:
import { useEffect, useEffectEvent } from 'react';
function ChatRoom({ roomId, theme }) {
const onConnected = useEffectEvent(() => {
showNotification('Connected!', theme); // always the latest theme
});
useEffect(() => {
const connection = createConnection(roomId);
connection.on('connected', () => onConnected());
connection.connect();
return () => connection.disconnect();
}, [roomId]); // theme is not a dependency
}
Fungsi dari useEffectEvent hanya untuk dipanggil di dalam effect. Jangan berikan ke komponen lain atau mencantumkannya di array dependensi.
Pertanyaan yang sering diajukan
Kapan useEffect berjalan?
Setelah React merender komponen dan memperbarui DOM, biasanya setelah browser menggambar layar. Tanpa array dependensi, effect berjalan setelah setiap render; dengan [], hanya setelah render pertama; dan dengan [a, b], setelah render pertama dan setelah setiap render saat a atau b berubah.
Apa itu fungsi cleanup di useEffect?
Fungsi yang kamu kembalikan dari effect. React memanggilnya sebelum menjalankan effect lagi dengan dependensi baru, dan sekali lagi saat komponen dihapus dari layar. Pakai untuk menghentikan timer, menghapus event listener, dan menutup subscription.
Mengapa useEffect saya berjalan dua kali?
Di development, <StrictMode> me-mount setiap komponen, menjalankan effect-nya, membersihkannya, lalu menjalankannya lagi, untuk menunjukkan effect yang tidak punya cleanup. Ini terjadi sekali saat mount dan hanya di development. Build production menjalankan effect sekali.
Mengapa useEffect saya menyebabkan perulangan tanpa akhir?
Effect itu mengatur nilai state yang juga ada di array dependensinya (atau tidak punya array sama sekali). Mengatur state memicu render lagi, dependensinya berubah, jadi effect berjalan lagi. Pakai fungsi updater dan hapus dependensinya, tambahkan kondisi, atau hitung nilainya saat render.
Bisakah callback useEffect berupa async?
Tidak. Callback harus tidak mengembalikan apa pun atau mengembalikan fungsi cleanup, sedangkan fungsi async mengembalikan promise. Definisikan fungsi async di dalam effect lalu panggil, seperti yang ditunjukkan di halaman mengambil data.