Suspense
Lazy Loading dersinde Suspense'i yalnızca lazy() ile birlikte
gördük. Bu ders, Suspense'in kendisine -- ne yaptığına, nasıl iç içe
kullanılabileceğine, ve neyi OTOMATİK OLARAK yapmadığına -- daha
yakından bakıyor.
fallback Prop'u
Suspense, İÇİNDEKİ bir şey HENÜZ hazır değilken bir fallback
gösterir:
import { lazy, Suspense } from "react";
const CourseDetails = lazy(() => import("./CourseDetails.jsx"));
function SuspenseFallbackExample() {
return (
// `fallback`, herhangi bir JSX olabilir -- yalnızca bir metin değil,
// bir spinner, bir iskelet (skeleton) ekran, ya da başka bir
// component de olabilir. Lazy Loading dersinde Suspense'i YALNIZCA
// lazy() ile birlikte gördük -- bu ders, Suspense'in kendisine daha
// yakından bakıyor.
<Suspense fallback={<p className="spinner">Loading course...</p>}>
<CourseDetails />
</Suspense>
);
}
fallback, yalnızca bir metin değil, HERHANGİ bir JSX olabilir -- bir
spinner, bir iskelet (skeleton) ekran, ya da başka bir component.
İçindeki component (burada CourseDetails) hazır olduğunda, fallback
otomatik olarak gerçek içerikle DEĞİŞTİRİLİR.
İç İçe Suspense Sınırları
Birden fazla Suspense, farklı seviyelerde iç içe kullanılabilir:
import { lazy, Suspense } from "react";
const CourseHeader = lazy(() => import("./CourseHeader.jsx"));
const CourseReviews = lazy(() => import("./CourseReviews.jsx"));
function NestedSuspenseExample() {
return (
// Dıştaki Suspense, CourseHeader yüklenene kadar TÜM sayfa için bir
// fallback gösterir. CourseHeader göründükten sonra, İÇTEKİ Suspense
// yalnızca CourseReviews'un yerini kaplar -- sayfanın geri kalanı
// (CourseHeader dahil) tekrar "loading" durumuna DÖNMEZ.
<Suspense fallback={<p>Loading page...</p>}>
<CourseHeader />
<Suspense fallback={<p>Loading reviews...</p>}>
<CourseReviews />
</Suspense>
</Suspense>
);
}
Dıştaki Suspense, CourseHeader yüklenene kadar TÜM sayfa için bir
fallback gösterir. CourseHeader göründükten sonra, İÇTEKİ Suspense
yalnızca CourseReviews'un yerini kaplar -- sayfanın geri kalanı
tekrar "loading" durumuna DÖNMEZ. Bu, kullanıcıya daha akıcı bir
deneyim sunar: her şeyin birden kaybolup gelmesi yerine, yalnızca
gerçekten bekleyen kısım "loading" gösterir.
use() Hook'u ile Suspense
React 19'daki use() hook'u, bir Promise'i doğrudan Suspense ile
entegre EDEBİLİR:
import { Suspense, use } from "react";
function fetchCourse() {
return fetch("http://localhost:3000/courses/1").then((response) => response.json());
}
// Her render'da YENİ bir Promise oluşturmamak için, bunu component'in
// DIŞINDA, modül yüklenirken bir kez çağırıyoruz.
const coursePromise = fetchCourse();
function CourseName() {
// use(), normal hook'ların aksine KOŞULLU olarak da çağrılabilir. Bir
// Promise verildiğinde, use() Promise HENÜZ çözülmediyse React'e
// "beklemem gerekiyor" der -- bu, en yakın Suspense'in fallback'ini
// gösterir; Promise çözülünce gerçek değeri döner.
const course = use(coursePromise);
return <p>Course: {course.name}</p>;
}
function UsePromiseWithSuspenseExample() {
return (
<Suspense fallback={<p>Loading course...</p>}>
<CourseName />
</Suspense>
);
}
use(), diğer hook'ların aksine KOŞULLU olarak da çağrılabilir. Bir
Promise verildiğinde, henüz ÇÖZÜLMEDİYSE React'e "beklemem gerekiyor"
der -- bu, en yakın Suspense'in fallback'ini gösterir; Promise
çözülünce use() gerçek değeri döner ve component normal render
edilir.
Suspense'in Otomatik Yapmadıkları
Önemli bir gotcha: her asenkron işlem, Suspense'i otomatik tetiklemez:
import { Suspense, useEffect, useState } from "react";
function CourseListWithEffect() {
const [courses, setCourses] = useState(null);
// ÖNEMLİ: useEffect + fetch (API & Data Fetching dersindeki desen),
// Suspense'i OTOMATİK OLARAK TETİKLEMEZ -- Suspense yalnızca use() gibi,
// React'in DOĞRUDAN tanıdığı bir Promise kaynağıyla çalışır. Bu yüzden
// burada `courses` state'i null'ken, dışarıdaki Suspense'in fallback'i
// GÖRÜNMEZ -- component `null` render eder, Suspense'in haberi bile
// olmaz.
useEffect(() => {
fetch("http://localhost:3000/courses")
.then((response) => response.json())
.then((data) => setCourses(data));
}, []);
if (!courses) {
// Suspense'in fallback'i değil, kendi manuel loading kontrolümüz.
return null;
}
return (
<ul>
{courses.map((course) => (
<li key={course.id}>{course.name}</li>
))}
</ul>
);
}
function SuspenseLimitationsExample() {
return (
<Suspense fallback={<p>This fallback never shows for CourseListWithEffect.</p>}>
<CourseListWithEffect />
</Suspense>
);
}
API & Data Fetching dersindeki useEffect + fetch deseni, Suspense'i
OTOMATİK OLARAK tetiklemez -- Suspense yalnızca use() gibi, React'in
DOĞRUDAN tanıdığı bir Promise kaynağıyla çalışır. useEffect içinde
fetch kullanan bir component, kendi loading state'ini KENDİSİ
yönetmeye devam etmeli.
Özet ve Terimler Sözlüğü
Suspense, içindeki bir şey henüz hazır değilken bir fallback
gösterir; içerik hazır olunca otomatik olarak değiştirilir. Birden
fazla Suspense, farklı granülaritede yükleme durumları göstermek için
iç içe kullanılabilir. React 19'daki use() hook'u, bir Promise'i
Suspense ile entegre eder. Ama useEffect + fetch gibi "klasik" veri
çekme desenleri Suspense'i OTOMATİK tetiklemez -- yalnızca use() gibi
React'in doğrudan desteklediği kaynaklar tetikler.
Terimler Sözlüğü
Suspense — İçindeki bir kaynak (lazy component, Promise) henüz hazır değilken bir fallback UI gösteren React component'i.
Suspense Boundary (Suspense Sınırı) — Bir <Suspense> component'inin
kapsadığı, kendi fallback'ine sahip alan.