nextjsrscaturanMenengah3 mnt baca

Batasan & Aturan Server-Client

Cheat sheet aturan: apa yang boleh lewat boundary dan apa yang tidak.

Tiga aturan boundary

Seluruh drama server vs client diringkas menjadi tiga aturan:

  1. Server → Client: hanya props yang serializable (string, number, boolean, plain object, array). Fungsi, Date, Map, class instance: tidak bisa lewat.
  2. Client → Server: tidak bisa mengimpor. Kalau butuh konten server di dalam client, pakai pola children/slot.
  3. Arah import: Server Component boleh mengimpor Client Component. Sebaliknya dilarang.

Data yang lewat boundary diserialisasi menjadi payload teks mirip JSON. Fungsi tidak bisa diserialisasi dengan aman, dan Date kehilangan prototype-nya.

Contoh 1: Date harus jadi string dulu

tsx
// app/blog/page.tsx (server)
// ❌ SALAH: oper objek Date langsung
import CommentList from "./CommentList";

export default async function BlogPage() {
  const posts = await db.post.findMany();
  return <CommentList posts={posts} now={new Date()} />;
}
tsx
// app/blog/page.tsx (server)
// ✅ BENAR: serialisasi di server, parsing di client
import CommentList from "./CommentList";

export default async function BlogPage() {
  const posts = await db.post.findMany();
  const plainPosts = posts.map((p) => ({
    ...p,
    createdAt: p.createdAt.toISOString(), // Date -> string
  }));
  return <CommentList posts={plainPosts} now={new Date().toISOString()} />;
}
tsx
// app/blog/CommentList.tsx
"use client";

export default function CommentList({
  posts,
  now,
}: {
  posts: { id: string; title: string; createdAt: string }[];
  now: string;
}) {
  const nowDate = new Date(now); // parsing kembali di client, aman
  return (
    <ul>
      {posts.map((p) => (
        <li key={p.id}>
          {p.title} · {new Date(p.createdAt).toLocaleDateString("id-ID")}
        </li>
      ))}
    </ul>
  );
}

Pola umumnya: sederhanakan data menjadi plain object selagi masih di server, bangun kembali tipe kaya (Date, dsb.) di client kalau perlu.

Contoh 2: fungsi tidak bisa jadi props, pindahkan logikanya

tsx
// app/todo/page.tsx (server)
// ❌ SALAH: oper fungsi handler ke client
import TodoList from "./TodoList";

export default async function TodoPage() {
  const todos = await db.todo.findMany();
  async function handleDelete(id: string) {
    "use server";
    await db.todo.delete({ where: { id } });
  }
  return <TodoList todos={todos} onDelete={handleDelete} />;
}

Catatan: fungsi yang ditandai "use server" (Server Action) memang boleh lewat boundary karena didaftarkan Next.js sebagai endpoint khusus. Yang dilarang adalah fungsi biasa yang tidak bisa diserialisasi. (Server Action dibahas tuntas di modul tersendiri.)

Kalau belum sampai ke Server Action, alternatif paling sederhana: pindahkan logika handler ke dalam Client Component itu sendiri.

tsx
// app/todo/TodoList.tsx
"use client";

import { useState } from "react";

export default function TodoList({
  todos,
}: {
  todos: { id: string; title: string }[];
}) {
  const [items, setItems] = useState(todos);

  async function handleDelete(id: string) {
    await fetch(`/api/todos/${id}`, { method: "DELETE" }); // via Route Handler
    setItems((prev) => prev.filter((t) => t.id !== id));
  }

  return (
    <ul>
      {items.map((t) => (
        <li key={t.id}>
          {t.title} <button onClick={() => handleDelete(t.id)}>Hapus</button>
        </li>
      ))}
    </ul>
  );
}

Katalog error umum

Pesan errorPenyebabSolusi
"You're importing a component that needs useState"Komponen butuh state tapi file-nya tanpa "use client"Tambahkan "use client" di baris pertama file itu
"Functions cannot be passed to Client Components"Mengoper fungsi biasa sebagai propsTandai sebagai Server Action ("use server") atau pindahkan logika ke client
"Only plain objects can be passed"Mengoper class instance, Map, atau DateSederhanakan jadi plain object/string di server
window is not definedBrowser API dipakai di Server ComponentPindahkan ke Client Component
Event handler (onClick) tidak jalan di serverHandler ditulis di Server ComponentEkstrak tombol/handler ke Client Component kecil

Tips debugging: baca pesan error overlay baik-baik, Next.js biasanya menyebut nama file pelanggarnya. Lalu tanyakan: "file ini server atau client?" dan "apa yang lewat boundary di sini?"

Tantangan

Perbaiki error

Sengaja buat error: oper new Date() dan sebuah fungsi ke Client Component. Baca pesan error-nya, lalu perbaiki dengan serialisasi yang benar.