Komponen React punya tugas utama: render UI dari state. Tapi banyak hal yang perlu jalan DI LUAR render — fetch data dari API, set timer, listen ke window resize, update document.title, manipulasi localStorage. Operasi ini punya nama: side effects. useEffect adalah hook yang ngasih kamu cara JALANIN side effect SECARA AMAN — di waktu yang tepat, dengan cleanup yang proper, tanpa ngeganggu render cycle React.
🤔 Kenapa gak langsung di body komponen?
function App() { fetch("/api/data"); ... }jalan tiap render = 1000 fetch dalam 10 detik = server kena flood.useEffectngasih kontrol KAPAN side effect dipanggil (mount, dependency change, atau unmount), bukan tiap render. Plus React tau cara cleanup-nya pas component unmount — gak ada memory leak.
import { useEffect } from "react"
function App() {
useEffect(() => {
console.log("Komponen dimuat!")
document.title = "Halaman Baru"
}, []) // [] = hanya sekali saat mount
return <p>Cek console dan judul tab!</p>
}
Dependency Array — Kapan Effect Jalan
| Pattern | Kapan dijalankan |
|---|---|
useEffect(() => {...}, []) |
Sekali saat mount (paling umum) |
useEffect(() => {...}, [count]) |
Saat count berubah |
useEffect(() => {...}, [a, b]) |
Saat a ATAU b berubah |
useEffect(() => {...}) |
Setiap render (jarang dipake — biasanya bug) |
Contoh: Sync state ke DOM
function App() {
const [count, setCount] = useState(0)
useEffect(() => {
document.title = `Hitungan: ${count}`
}, [count]) // re-run tiap count berubah
return <button onClick={() => setCount(c => c + 1)}>+1</button>
}
Contoh: Fetch Data
function UserList() {
const [users, setUsers] = useState([])
const [loading, setLoading] = useState(true)
useEffect(() => {
fetch("/api/users")
.then(r => r.json())
.then(data => {
setUsers(data)
setLoading(false)
})
}, []) // sekali saat mount
if (loading) return <p>Loading...</p>
return <ul>{users.map(u => <li key={u.id}>{u.name}</li>)}</ul>
}
🎭 Analogi sehari-hari
useEffect itu kayak alarm di HP kamu. Alarm harian (useEffect(..., [])) = setel sekali, bunyi tiap pagi (setelah mount). Alarm reminder ulang tahun teman (useEffect(..., [tanggal])) = bunyi cuma pas tanggal tertentu cocok. Alarm gak boleh dipanggil di tengah aktivitas masak (render) — bisa rusak proses. HP nge-handle scheduling otomatis: "kapan bunyi, kapan stop, kapan reset". useEffect sama: kasih instruksi (effect function), kasih kondisi kapan jalan (deps array), React urus timing-nya. Plus pas HP mati (component unmount), alarm otomatis di-clear — itu cleanup function (lesson selanjutnya).
⚠️ Jebakan yang sering ditemui
- Lupa dependency array —
useEffect(() => {})jalan TIAP render = bisa infinite loop kalau effect-nyasetState. WAJIB kasih array. - Empty deps
[]tapi pakai variable di luar —useEffect(() => fetchUser(userId), [])—userIdberubah, effect gak re-run. ESLint pluginreact-hooks/exhaustive-depscatch ini. - Object/array di deps —
useEffect(..., [{x: 1}])— object literal beda reference tiap render = effect re-run terus. Pakai primitive:[user.id]bukan[user]. - Fetch tanpa cleanup — component unmount sebelum fetch selesai → setState di unmounted = warning. Pakai cleanup pattern (lesson selanjutnya: useEffect Lanjutan).
- Sync state berdasarkan props/state lain —
useEffect(() => setX(y * 2), [y])= anti-pattern. Hitung langsung:const x = y * 2. Effect cuma buat side effect ke DUNIA LUAR, bukan derive state. asynclangsung di useEffect —useEffect(async () => {...})= ERROR (effect harus return cleanup atau undefined, bukan Promise). Bungkus inner:useEffect(() => { async function load() {...}; load() }, []).- Multiple effects nge-set state berurutan — bikin re-render berlapis + flicker. Combine di satu effect kalau related.
🎯 Butuh
useEffect? Atau bisa pakai cara lain?
- Sync ke browser API (document.title, localStorage, scroll position) →
useEffect✓- Subscribe ke event (window resize, websocket, timer) →
useEffect✓ + cleanup- Fetch data →
useEffect✓ (lebih baik: TanStack Query / SWR / RSC)- Hitung nilai dari props/state → JANGAN useEffect, hitung langsung di body component
- Sync state turunan (e.g.,
fullName = first + last) → JANGAN useEffect, derive langsung- Reset state pas prop berubah → bisa key prop di parent (lebih clean), atau useEffect dengan deps prop
- Run sekali saat klik tombol → JANGAN useEffect, panggil di event handler langsung
Aturan: pertanyaan pertama "ini bener-bener side effect?" — kalau jawabannya bisa dihitung dari state/props, jangan pakai useEffect. React 19 mulai banyak push pattern "less useEffect".
TL;DR: useEffect(fn, deps) = jalanin side effect (fetch, DOM, timer, subscription) di luar render cycle. [] = sekali saat mount. [x, y] = saat x atau y berubah. Tanpa deps = tiap render (jarang). Effect TIDAK boleh async langsung — bungkus inner function. Object/array di deps bikin re-run terus (pakai primitive). Hitung nilai dari state/props? JANGAN useEffect, derive langsung. Modern alternatif fetch: TanStack Query/SWR.