Lompat ke konten
Kembali ke modul

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 ComponentClient Component
DefaultYaTidak — butuh "use client"
Kodenya dikirim ke browserTidak pernahYa, seluruhnya
Bisa query databaseYa, langsungTidak
Bisa membaca rahasia process.envYaTidak
Bisa await di level teratasYaTidak
Bisa memakai useState, useEffectTidakYa
Bisa memakai onClickTidakYa
Bisa membaca cookieYaHanya 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.

Coba sendiri

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.

tsx
// 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>
    </>
  );
}
Dorong "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.

tsx
// 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>
Pola terakhir itu adalah cara pembungkus client bisa memuat konten server: server me-render-nya lebih dulu lalu mengirim hasil jadinya.

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

Kamu menambahkan "use client" ke sebuah file layout. Apa yang terjadi?

Pilih jawaban dulu