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.
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
// 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>;
}<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.