09 / 12
Suspense ve hata sınırları
Yükleme durumunu Suspense ile tanımlamak, use ile Promise okumak, boundary yerleşimi ve Error Boundary.
Yükleme Durumunu Ağaçta Tanımlamak
Her component'te isLoading state'i tutup spinner göstermek yerine React'e "bu bölüm hazır olana kadar şunu göster" dersiniz. Bu sınıra Suspense boundary denir:1
<Suspense fallback={<ProductSkeleton />}>
<ProductDetails id={productId} />
</Suspense>İçerideki herhangi bir component henüz hazır değilse React en yakın boundary'nin fallback'ini gösterir, hazır olunca içeriği açar. Bir boundary'yi şunlar bekletir:1
lazyile yüklenen component kodu.useile okunan bir Promise; Server Components'ten gelen veri ya da Suspense destekleyen bir framework ve kütüphane (ör. TanStack Query'ninuseSuspenseQuery'si) dahil.precedenceverilmiş bir stylesheet.
Warning
Suspense, Effect içinde çekilen veriyi algılamaz. useEffect + setState ile veri çekiyorsanız boundary hiçbir şey beklemez.1
use ile Promise Okumak
use, bir Promise'in değerini render sırasında okur. Promise bekliyorsa component askıya alınır ve Suspense devreye girer; reddedilirse en yakın Error Boundary'ye gider.2
import { Suspense, use } from "react";
const cache = new Map<string, Promise<Product>>();
export function fetchProduct(id: string) {
let promise = cache.get(id);
if (!promise) {
promise = fetch(`/api/products/${id}`).then((r) => r.json() as Promise<Product>);
cache.set(id, promise);
}
return promise;
}
function ProductDetails({ id }: { id: string }) {
const product = use(fetchProduct(id));
return <h1>{product.name}</h1>;
}En önemli kural: Promise cache'lenmeli. Render içinde use(fetch(...)) yazarsanız her render yeni bir Promise oluşturur ve fallback sürekli tekrar görünür.2
Yukarıdaki Map, kuralı göstermek için yeterli ama gerçek bir cache değildir: hatalı isteği tekrar denemez, veriyi tazelemez, hiçbir şeyi silmez. React dokümanı da Promise'leri framework'ün ya da Suspense destekli bir kütüphanenin yönetmesini önerir.21 Pratikte use'u çoğunlukla Next.js'te Server Component'in client component'e geçirdiği Promise'i okurken ya da bir kütüphanenin içinde görürsünüz.
Boundary'ler Nereye Konur
Boundary'ler bir tasarım kararıdır. React dokümanının önerisi: her component'i ayrı ayrı sarmayın; kullanıcının görmesini beklediği yükleme sırasını tasarlayın ve boundary'leri ona göre yerleştirin.1
<Suspense fallback={<PageSkeleton />}>
<ProductHeader id={id} />
<ProductGallery id={id} />
<Suspense fallback={<ReviewsSkeleton />}>
<Reviews id={id} />
</Suspense>
</Suspense>Başlık ve galeri birlikte açılır; yorumlar daha yavaşsa sayfayı bekletmez, kendi iskeletini gösterir. Her parça ayrı bir boundary'de olsaydı ekran parça parça "patlayarak" dolardı (popcorn UI).
Waterfall'a dikkat. React 19'da bir component askıya alındığında React fallback'i hemen gösterir, kardeşleri sonra hazırlar. TkDodo, bu değişiklikle aynı boundary'deki kardeşlerin kendi verisini render sırasında başlattığı kodlarda isteklerin paralel değil sıralı gidebildiğini anlatıyor.3 React ekibinin önerisi aynı: veriyi component'in derininde değil, olabildiğince erken başlatın; route loader'da, sunucuda ya da ebeveynde.4
Zaten görünen içeriği gizlemeyin. Kullanıcı bir sekmeye tıkladığında sayfanın yerini aniden iskelet alıyorsa güncellemeyi startTransition ile yapın; React eski içeriği gösterirken yenisini arka planda hazırlar. Router'lar navigasyonu zaten transition olarak yapar.1
Kod Bölme: lazy
Bir component'i ancak gerektiğinde yüklemek için lazy kullanılır; yüklenirken en yakın Suspense'in fallback'i görünür:5
import { lazy, Suspense } from "react";
const SizeGuide = lazy(() => import("./SizeGuide.tsx"));
{isGuideOpen && (
<Suspense fallback={<Spinner />}>
<SizeGuide />
</Suspense>
)}lazy her zaman component dışında, modül seviyesinde çağrılır. Ne zaman bölmeye değdiğini Bundle boyutu dersi anlatıyor.
Error Boundary
Render sırasında fırlatılan bir hata, bir Error Boundary yoksa tüm uygulamayı boş ekrana çevirir. Error Boundary bugün hâlâ yalnızca class component olarak yazılabilir; React dokümanı bunun yerine react-error-boundary paketini önerir.6
$ pnpm add react-error-boundaryimport { ErrorBoundary } from "react-error-boundary";
<ErrorBoundary
fallbackRender={({ resetErrorBoundary }) => (
<div role="alert">
<p>Ürün yüklenemedi.</p>
<button onClick={resetErrorBoundary}>Tekrar dene</button>
</div>
)}
resetKeys={[productId]}
>
<Suspense fallback={<ProductSkeleton />}>
<ProductDetails id={productId} />
</Suspense>
</ErrorBoundary>resetKeys değiştiğinde (kullanıcı başka ürüne geçtiğinde) boundary kendini sıfırlar.7 Error Boundary'yi de Suspense gibi tasarıma göre yerleştirin: yorumlar hata verdiğinde ürün sayfası çökmemeli.
Error Boundary her hatayı yakalamaz. Event handler'larda ve render bittikten sonra çalışan asenkron kodda fırlatılan hatalar onun dışındadır.6 Bunları ya state'e yazıp gösterin ya da useErrorBoundary'nin showBoundary fonksiyonuyla boundary'ye iletin.7 Kent C. Dodds bu paketi ve showBoundary kalıbını ayrı bir yazıda anlatıyor.8 startTransition içindeki Action'larda fırlatılan hatalar ise istisnadır, Error Boundary'ye gider; bunu bir sonraki derste göreceksiniz.9
Don't
JSX'i
try/catchile sarmak. Render hatası orada yakalanmaz;eslint-plugin-react-hooks'unerror-boundarieskuralı bunu uyarır.10
Yalnızca Tarayıcıda Render
Server rendering kullanan bir uygulamada bazı component'ler sunucuda anlamlı bir şey üretemez (localStorage, kullanıcının saat dilimi). React 19.3 bunun için use(browser()) ekledi: component sunucuda askıya alınır, HTML'e en yakın Suspense'in fallback'i girer, tarayıcıda normal render olur. Eskiden kullanılan mounted state'i + Effect kalıbının yerini alır.11 Vite ile yazılan bir SPA'da sunucu render'ı olmadığı için buna ihtiyacınız olmaz.
Kendin Dene
ProductDetails'teuse(fetchProduct(id))yerineuse(fetch(...).then(...))yazın ve ne olduğunu izleyin.Sahte API'ye 2 saniye gecikme ekleyin. Yorumları önce aynı boundary'de, sonra ayrı bir boundary'de deneyin.
API'nin hata döndürmesini sağlayın; Error Boundary olmadan ve varken sayfayı karşılaştırın.
"Tekrar dene" butonu neden hep aynı hatayı veriyor? (İpucu: cache.) Hatalı Promise'i cache'ten silen bir düzeltme yazın.
Detaylı Öğrenmek İçin
Resmi doküman:
<Suspense>referansı; boundary'leri birlikte ya da iç içe açmak, eski içeriği göstermek ve navigasyonda sıfırlamak.1Yazı: TkDodo'nun React 19'daki Suspense değişikliğini anlattığı "A Drama in 3 Acts" yazısı.3
Video: Ricky Hanlon'ın "Async React" konuşması; Suspense, transition ve optimistic state'in birlikte nasıl çalıştığı (React Conf 2025, 1. gün).12
Yazı: Kent C. Dodds'un
react-error-boundaryyazısı.8
Kaynaklar
Suspense'i neyin etkinleştirdiği, Effect'teki verinin algılanmaması, boundary yerleşimi ve transition ile eski içeriği gösterme.
Promise okuma, cache'lenmemiş Promise uyarısı ve Error Boundary ile hata gösterme.
: — 16 Haziran 2024. Kardeşlerin önceden render edilmemesi ve paralel isteklerin sıralı hâle gelmesi.
Component içinde veri çekmenin waterfall'a yol açması; router loader'da ya da sunucuda önceden çekme önerisi.
Component kodunu ilk render'da yüklemek ve modül seviyesinde tanımlamak.
Error Boundary'nin class gerektirmesi, yakalamadığı hatalar ve `react-error-boundary` önerisi.
`fallbackRender`, `resetKeys` ve `useErrorBoundary`. Sürüm 6.1 ile kontrol edildi.
: — 20 Temmuz 2020. Paketin kullanımı ve event handler hatalarını boundary'ye iletmek.
Transition içindeki hataların Error Boundary ile gösterilmesi.
Çocuk component hataları için `try/catch` yerine Error Boundary.
React 19.3'teki `use(browser())`; sunucuda askıya alma ve Suspense şartı.
1. gün yayını. Transition, Suspense ve optimistic güncellemelerle async bir arayüz.
Last updated: October 21, 2026
