Kenapa Render Tidak Berhenti dan Cara Mengatasi useEffect Infinite Loop Next Js

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:

  1. Isolasi efek bermasalah dengan mematikan efek lain sementara dan tambah log satu baris di awal efek.
  2. Stabilkan dependensi dengan memakai primitif seperti query, bukan objek filter.
  3. Pindahkan setState yang tidak berkaitan dengan efek ke event handler atau hitung langsung saat render.
  4. 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.