Lompat ke konten
Kembali ke modul

Next.js: Fullstack dalam Satu Proyek · 4/9

Loading state, streaming, dan Suspense

Tampilkan halaman sebelum bagian yang lambat siap, alih-alih spinner menutupi semuanya.

Baca 25 menit

Setelah pelajaran ini kamu bisa

  • Menjelaskan apa yang dikirim streaming dan kapan
  • Memakai `loading.tsx` dan `<Suspense>` dengan sadar
  • Memilih di mana menempatkan sebuah boundary
  • Menjelaskan kenapa satu query lambat tidak boleh memblokir seluruh halaman

Tanpa streaming, server menunggu setiap potongan data sebelum mengirim apa pun, dan penggunanya menatap halaman kosong. Streaming mengirim halamannya secara bertahap: kerangkanya langsung, dan setiap bagian yang lambat saat ia siap.

Coba sendiri

Halaman yang sama, blocking versus streaming. Perhatikan kapan pengguna pertama kali melihat sesuatu.

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.

Dua cara mendapatkannya

tsx
// 1. loading.tsx - a whole-segment boundary, written for you.
// app/[lang]/dashboard/loading.tsx
export default function Loading() {
  return <Skeleton />;      // shown while dashboard/page.tsx loads
}

// 2. <Suspense> - a boundary exactly where you want one.
import { Suspense } from "react";

export default async function VenuePage() {
  const venue = await getVenue();          // fast: the page waits for this

  return (
    <>
      <h1>{venue.name}</h1>

      {/* Slow, and not essential to the page's purpose. */}
      <Suspense fallback={<ReviewsSkeleton />}>
        <Reviews venueId={venue.id} />
      </Suspense>

      <Suspense fallback={<p>Loading similar venues...</p>}>
        <SimilarVenues venueId={venue.id} />
      </Suspense>
    </>
  );
}

// The awaiting happens INSIDE the boundary, which is what makes it work.
async function Reviews({ venueId }) {
  const reviews = await getReviews(venueId);   // slow
  return <ul>{reviews.map((r) => <li key={r.id}>{r.text}</li>)}</ul>;
}
Dua blok <Suspense> itu independen: review yang datang terlambat tidak menahan venue serupa.
  • Taruh boundary di sekitar hal yang lambat dan tidak esensial. Review, rekomendasi, panel analitik.
  • Biarkan konten utamanya di luar. Kalau halamannya tentang sebuah venue, menunggu venue-nya itu benar — kerangka tanpa venue tidak berguna.
  • Buat fallback-nya sesuai layout aslinya. Skeleton seukuran kontennya mencegah halaman melompat saat konten datang.
  • Jangan membungkus semuanya. Setiap boundary adalah sebuah spinner, dan halaman dengan enam spinner terasa lebih buruk daripada satu penantian singkat.

Tugas praktik

Tambahkan loading.tsx ke route dashboard proyek ini dengan teks yang terlihat seperti "Memuat dasbor…". Lalu batasi jaringanmu di DevTools (Network → Slow 3G) dan buka halamannya. Kamu seharusnya melihat fallback-nya. Lalu pindahkan boundary <Suspense> hanya di sekitar tabel tim dan pastikan progres pribadinya muncul lebih dulu.

Hasil yang diharapkan

Fallback-nya muncul, lalu digantikan oleh konten sebenarnya.