Render yang tidak berhenti di Next.js App Router hampir selalu berasal dari Client Component yang memanggil useEffect lalu mengubah state yang juga menjadi dependensi efek tersebut. Perbaikannya: stabilkan dependensi, pindahkan setState yang tidak perlu ke luar efek, dan gunakan functional update supaya efek tidak memicu render baru tanpa henti.
Penyebab useEffect Jalan Terus di Client Component
Server Component tidak menjalankan hook. Kode useEffect hanya hidup setelah file diberi "use client" dan dirender browser.
Masalah muncul karena setiap render membuat referensi baru untuk objek, array, dan fungsi inline. React membandingkan dependensi dengan Object.is, sehingga referensi baru dianggap berubah dan efek dijalankan lagi.
Pemicu paling sering adalah setState tanpa kondisi henti di dalam efek. Efek mengubah state, state memicu render, render memicu efek lagi.
Tabel Diagnosis Cepat Infinite Loop
| Gejala | Penyebab | Cek awal |
|---|---|---|
| Komponen berkedip, CPU naik | setState tanpa guard | console.log sebelum setState |
| Network fetch berulang | dependensi objek inline | cek tab Network dan array deps |
| Loop hanya setelah interaksi | fungsi tanpa useCallback | cek React DevTools Profiler |
Cara Memperbaiki Dependensi dan State secara Bertahap
Sebelum perbaikan, pola bermasalah sering terlihat seperti ini:
"use client";
import { useEffect, useState } from "react";
export default function List({ query }) {
const [items, setItems] = useState([]);
const filter = { q: query };
useEffect(() => {
fetch(`/api/items?q=${filter.q}`)
.then((r) => r.json())
.then(setItems);
}, [filter]);
return <ul>{items.map((i) => <li key={i.id}>{i.name}</li>)}</ul>;
}
Lakukan perbaikan dengan urutan ini:
- Isolasi efek bermasalah dengan mematikan efek lain sementara dan tambah log satu baris di awal efek.
- Stabilkan dependensi dengan memakai primitif seperti
query, bukan objekfilter. - Pindahkan
setStateyang tidak berkaitan dengan efek ke event handler atau hitung langsung saat render. - Pakai functional update
setItems((prev) => ...)bila nilai baru bergantung nilai lama.
Sesudah perbaikan:
useEffect(() => {
let stop = false;
fetch(`/api/items?q=${query}`)
.then((r) => r.json())
.then((data) => {
if (!stop) setItems(data);
});
return () => { stop = true; };
}, [query]);
- Fetch di efek tanpa guard pembatalan sehingga response lama menimpa state baru.
- Object atau array inline dimasukkan ke dependency array.
- Fungsi dari parent diteruskan langsung sebagai dependensi tanpa
useCallback.
FAQ
Apakah useEffect boleh fetch data di App Router?
Boleh untuk data interaktif di Client Component, sedangkan data awal sebaiknya diambil di Server Component.
Kapan dependensi boleh dikosongkan?
Hanya bila efek benar-benar tidak membaca props atau state yang bisa berubah.
Kenapa loop muncul hanya di production?
Strict Mode di development menutupi urutan efek ganda, sedangkan data cache dan timing production membuat loop terlihat jelas.