Suspense

Suspense'in fallback prop'u, iç içe Suspense sınırları, React 19'un use() hook'u ile Suspense entegrasyonu, ve Suspense'in otomatik yapmadıkları -- basit örneklerle.

İleri 11 dk
EN

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.