Next.js: Fullstack dalam Satu Proyek · 2/9
Server Component dan Client Component
Keputusan paling penting di aplikasi Next.js, dan arti sebenarnya dari `"use client"`.
Baca 32 menit
Setelah pelajaran ini kamu bisa
- Menentukan untuk setiap component apakah ia sebaiknya Server atau Client Component
- Menjelaskan apa yang sebenarnya dilakukan `"use client"`
- Menjelaskan kenapa Server Component tidak bisa memakai `useState`
- Mengirim data melewati batasnya dengan benar
Ini keputusan paling penting di aplikasi Next.js, dan ini garis client/server dari modul 1 — sekarang jadi pilihan per component.
Semuanya adalah Server Component secara default. Ia di-render di server, dan kodenya tidak pernah dikirim ke browser. "use client" di bagian atas sebuah file membuat file itu dan semua yang diimpornya masuk ke bundle browser.
| Server Component | Client Component | |
|---|---|---|
| Default | Ya | Tidak — butuh "use client" |
| Kodenya dikirim ke browser | Tidak pernah | Ya, seluruhnya |
| Bisa query database | Ya, langsung | Tidak |
Bisa membaca rahasia process.env | Ya | Tidak |
Bisa await di level teratas | Ya | Tidak |
Bisa memakai useState, useEffect | Tidak | Ya |
Bisa memakai onClick | Tidak | Ya |
| Bisa membaca cookie | Ya | Hanya yang bukan httpOnly |
Alasan Server Component tidak bisa menyimpan state itu sederhana: ia jalan sekali, di server, lalu selesai. Tidak ada component yang tersisa di browser untuk di-render ulang. State dan event handler butuh sesuatu yang masih hidup di mesin penggunanya.
Keputusannya, sebagai sebuah function. Beri ia kebutuhan dan ia menyebutkan jenis mana yang kamu butuhkan — dan apa akibatnya kalau salah.
Hasil
Tekan Jalankan untuk melihat hasilnya.
Ini jalan di browser kamu, di dalam sandbox. Apa pun yang kamu tulis di sini tidak bisa merusak situs.
Hasil BookingForm adalah pola yang layak dipelajari. Saat sesuatu membutuhkan keduanya, kamu tidak memilih salah satu — kamu memisahkannya. Bagian server mengambil dan mengubah data; bagian client kecil menangani interaksinya.
// app/[lang]/venues/page.tsx - a Server Component
import { getVenues } from "@/features/venue/queries";
import { VenueFilter } from "./venue-filter"; // the client part
export default async function VenuesPage() {
const venues = await getVenues(); // database, on the server
return (
<>
<h1>{venues.length} venues</h1>
<VenueFilter venues={venues} /> {/* only this ships to the browser */}
</>
);
}
// ./venue-filter.tsx
"use client";
import { useState } from "react";
export function VenueFilter({ venues }) {
const [query, setQuery] = useState("");
const shown = venues.filter((v) => v.name.includes(query));
return (
<>
<input value={query} onChange={(e) => setQuery(e.target.value)} />
<ul>{shown.map((v) => <li key={v.id}>{v.name}</li>)}</ul>
</>
);
}"use client" sejauh mungkin ke bawah pohon. Batasnya adalah tempat bundle-nya mulai, jadi "use client" di sebuah layout akan mengirim seluruh halamannya.Menyeberangi batasnya
Props yang dikirim dari Server Component ke Client Component harus serializable — mereka berjalan lewat jaringan sebagai data. Function, instance class, dan Date beserta method-nya tidak lolos; object biasa, array, string, angka, dan Server Action lolos.
// Fine: plain data.
<VenueFilter venues={venues} count={42} locale="en" />
// Fails: a function is not serialisable.
<VenueFilter onSort={(a, b) => a.price - b.price} />
// Error: Functions cannot be passed directly to Client Components
// Fine: a Server Action is the sanctioned exception. React sends a reference,
// and calling it in the browser makes a POST back to the server.
<DeleteButton action={deleteVenue} />
// Fine: a Server Component passed as children into a Client Component.
<ClientShell>
<ServerRenderedContent />
</ClientShell>Tugas praktik
Di proyek ini, temukan setiap file yang diawali "use client" — ada lima. Untuk masing-masingnya, sebutkan satu kemampuan yang memaksanya. Lalu buka app/[lang]/modules/[module]/[lesson]/page.tsx, sebuah Server Component, dan perhatikan ia me-render LessonCompleteButton, sebuah Client Component, dengan hanya mengirim string. Jelaskan kenapa itu menjaga halaman pelajarannya tetap bisa di-prerender.
Hasil yang diharapkan
Halamannya sendiri tidak pernah membaca cookie, jadi tidak ada yang memaksanya jadi dinamis.
Cek pemahaman
Pilih jawaban dulu