Stop Loop Render: Cara Mengatasi Client Component Rerender Terus di App Router

Loop render di App Router hampir selalu berasal dari Client Component yang mengubah state lalu memicu efek yang sama berulang kali. Hentikan dulu siklusnya: kunci useEffect dengan deps stabil, jangan membuat objek atau array baru di setiap render, dan jangan meletakkan setState di dalam efek yang bergantung pada state itu sendiri.

Penyebab Client Component Rerender Terus di App Router

React me-render ulang saat state, props, atau context berubah. Masalah muncul ketika nilai itu terlihat baru padahal isinya sama.

  • State diinisialisasi dengan {}, [], new Date(), atau Math.random() langsung di body komponen.
  • useEffect tanpa array deps atau memakai deps objek baru, lalu memanggil fetch dan setState.
  • Fungsi handler dan objek props dibuat ulang setiap render, lalu dibaca ulang oleh komponen anak.
  • Context value dibuat inline sehingga semua konsumen ikut render ulang.
  • Pembacaan useSearchParams atau router yang berubah memicu fetch berantai di komponen client besar.

Tabel Diagnosis Rerender Berulang

Bedakan render wajar karena input pengguna dengan loop karena efek dan referensi tidak stabil.

Gejala Pemicu Pemeriksaan cepat
Fetch jalan terus Deps efek tidak stabil Cek array deps dan log URL fetch
Input lag atau flicker State besar di komponen induk Pecah input ke komponen kecil
CPU naik, tab berat Objek baru tiap render Cari literal objek di props
Loop tanpa henti Efek mengubah deps-nya Hapus setState dari efek

Cara Mengatasi Client Component Rerender Terus Langkah per Langkah

Validasi dengan React DevTools Profiler dulu sebelum menambah memo. Catat komponen yang render berulang, lalu perbaiki sumbernya.

  1. Isolasi bagian interaktif kecil sebagai Client Component di bawah Server Component. Biarkan data statis diambil di server.
  2. Perbaiki useEffect: beri deps minimal, pindahkan fetch keluar bila tidak perlu efek, dan hindari setState yang memicu efek yang sama.
  3. Stabilkan props dengan useMemo untuk objek, useCallback untuk handler, dan memo hanya untuk komponen anak yang render-nya mahal.
  4. Uji lagi di Profiler. Render harus turun tanpa mengubah hasil data.
"use client";
import { useEffect, useState } from "react";

export default function UserList({ query }: { query: string }) {
  const [users, setUsers] = useState<string[]>([]);

  useEffect(() => {
    let active = true;
    async function load() {
      const res = await fetch(`/api/users?query=${query}`);
      const data = await res.json();
      if (active) setUsers(data.users);
    }
    load();
    return () => {
      active = false;
    };
  }, [query]);

  return (
    <ul>
      {users.map((u) => (
        <li key={u}>{u}</li>
      ))}
    </ul>
  );
}

FAQ

Apakah useMemo selalu menghentikan rerender?

Tidak. Ia hanya membantu bila referensi props stabil dan render anak memang mahal.

Kenapa data fetch berulang terus?

Biasanya efek jalan ulang karena deps tidak stabil atau setState memicu efek yang sama lagi.

Kapan pisah ke Server Component?

Saat data bisa diambil di server dan hanya tombol, input, atau interaksi kecil yang butuh client.