Cara Menggunakan useEffect di Next Js untuk Data dan Event Browser

Untuk memakai useEffect di Next.js App Router, tandai file dengan ‘use client’ di baris pertama, lalu panggil fetch atau event browser di dalam useEffect. Hook ini tidak jalan di Server Component karena hook hanya hidup setelah React terhidrasi di browser. Langkah yang paling sering terlewat adalah cleanup dengan AbortController atau removeEventListener agar request dan listener tidak bocor saat pindah halaman.

Aturan Wajib useEffect Hanya Jalan di Client Component

Next.js merender Server Component di server tanpa DOM dan tanpa lifecycle browser. useEffect butuh browser, jadi file yang memakainya wajib Client Component.

  • Tambahkan ‘use client’ sebagai baris paling atas sebelum import.
  • Import dari React: import { useEffect, useState } from ‘react’.
  • Jangan akses database, headers, cookies server, atau secret langsung dari effect ini.
'use client';
import { useEffect, useState } from 'react';

export default function StatusOnline() {
  const [online, setOnline] = useState(true);
  useEffect(() => {
    const update = () => setOnline(navigator.onLine);
    window.addEventListener('online', update);
    window.addEventListener('offline', update);
    return () => {
      window.removeEventListener('online', update);
      window.removeEventListener('offline', update);
    };
  }, []);
  return <p>{online ? 'Online' : 'Offline'}</p>;
}

Langkah Fetch Data dan Cleanup dengan useEffect

Gunakan pola ini untuk data yang memang harus diambil dari browser, misalnya setelah interaksi user atau akses localStorage.

  1. Tandai file dengan ‘use client’.
  2. <li Panggil fetch di dalam useEffect, bukan di body komponen.

  3. Atur state loading, data, dan error secara eksplisit.
'use client';
import { useEffect, useState } from 'react';

export default function DaftarUser() {
  const [data, setData] = useState([]);
  const [error, setError] = useState(null);

  useEffect(() => {
    const controller = new AbortController();
    async function load() {
      try {
        const res = await fetch('/api/users', { signal: controller.signal });
        if (!res.ok) throw new Error('Gagal memuat data');
        setData(await res.json());
      } catch (err) {
        if (err.name !== 'AbortError') setError(err.message);
      }
    }
    load();
    return () => controller.abort();
  }, []);

  if (error) return <p>{error}</p>;
  return <ul>{data.map((u) => <li key={u.id}>{u.name}</li>)}</ul>;
}
  • Array kosong []: jalan sekali setelah mount, cocok untuk listener dan fetch awal di client.
  • Array terisi [query]: jalan ulang saat nilai berubah, cocok untuk pencarian.
  • Tanpa array: jalan setiap render, hampir selalu menyebabkan fetch berulang.

Pola Aman vs Pola Keliru

Kasus Penyebab Pola Benar
Effect jalan berulang Tanpa dependency array Tambahkan [] atau [query]
Fungsi async langsung di effect Return promise merusak cleanup Buat fungsi async di dalam effect
Listener menumpuk Tidak ada cleanup Kembalikan removeEventListener
Fetch awal lambat Semua data diambil dari client Pakai Async Server Component

Untuk data awal halaman, biarkan Server Component mengambilnya lalu teruskan sebagai props. Simpan useEffect untuk window, document, media query, IntersectionObserver, dan event browser lain.

FAQ

Apakah useEffect bisa dipakai di Server Component Next Js?

Tidak. Pindahkan logika effect ke Client Component yang diimpor dari Server Component.

Kenapa useEffect jalan dua kali di development?

Itu Strict Mode React untuk menguji cleanup, jadi pastikan return cleanup menutup request dan listener.

Kapan tidak perlu useEffect?

Untuk fetch awal halaman, gunakan Async Server Component agar data tersedia sebelum HTML dikirim.