use, bir bileşen render olurken bir promise'ten ya da bir context'ten bir değer okuyan bir React 19 API'sidir. use(promise), bileşeni promise resolve olana kadar duraklatır ve bu arada en yakın <Suspense> yedek içeriğini gösterir; use(context) ise useContext gibi çalışır ama bir if'in ya da bir döngünün içinde durabilir.
Aşağıdaki fetchUser fonksiyonu gerçek bir isteğin yerine geçer.
İlk render bir saniye boyunca "Loading..." gösterir, sonra profili. User 2'ye tıkla: yeni promise beklemedeyken yedek içerik geri gelir. Profile'ın kendine ait bir yükleme state'i yoktur; veri zaten oradaymış gibi yazılmıştır.
use(promise) nasıl çalışır
Profile, use(userPromise) çağırdığında:
- Promise resolve olduysa
usedeğerini döndürür ve render devam eder. - Hâlâ beklemedeyse React
Profile'ı render etmeyi durdurur ve üstündeki en yakın<Suspense>'in yedek içeriğini gösterir. Promise sonuçlandığında ReactProfile'ı yeniden render eder ve bu seferusedeğeri döndürür. - Reddedildiyse React bunun yerine en yakın error boundary'yi gösterir.
React sonucu promise nesnesinin kendisinde hatırlar. Promise'in bir sonraki render'da aynı nesne olması gerekmesinin nedeni budur.
Sonraki profil yüklenirken eski profili ekranda tutmak için promise'i bir transition içinde ayarla. React o zaman yedek içeriği yeniden göstermek yerine bekler:
<button onClick={() => startTransition(() => setUserPromise(fetchUser(2)))}>User 2</button>
Promise nerede oluşturulmalı
İnsanları tökezleten kısım budur. Veri çekme çağrısını onu okuyan bileşenin içine yazmak doğal görünür ve çalışmaz:
// Do not do this
function Profile({ id }) {
const user = use(fetchUser(id)); // a new promise on every render
return <p>{user.name}</p>;
}
Her render fetchUser'ı çağırır ve yeni, beklemede bir promise alır. Profile askıya alınır. O promise resolve olduğunda React Profile'ı yeniden render eder, bu da beklemede başka bir promise oluşturur ve yeniden askıya alınır. Bileşen veriyi hiç göstermez ve ağ sekmesi isteklerle dolar.
Promise'i okuyucunun her render'ında yeniden çalışmayan bir yerde oluştur:
- Bir ebeveynde, state'te tutulan (ilk örnekteki gibi) ya da bir olay işleyicisinde oluşturulan.
- Modül düzeyinde, veri props'a bağlı olmadığında: bileşenin üstünde
const configPromise = fetchConfig();. - Argümanlara göre anahtarlanmış bir önbellekte, böylece aynı id aynı promise'i döndürür.
- Bir framework'te: bir Server Component bir istemci bileşenine bir promise'i prop olarak aktarabilir, rota loader'ları da aynısını yapar.
use'un tasarlandığı durum budur.
İşte önbellek sürümü; böylece bir bileşen veriyi id ile isteyip yine de sabit bir promise alabilir:
Üç şehir arasında tıkla, sonra zaten açtığın birine geri dön. Konsol şehir başına bir istek kaydeder ve daha önce gördüğün bir şehir yedek içerik olmadan görünür, çünkü promise'i zaten resolve olmuştur. if (!cache.has(city)) kontrolünü sil; bileşen yeniden sonsuza kadar askıya alınmaya başlar.
Gerçek bir önbelleğin girişleri sona erdirmenin bir yoluna da ihtiyacı vardır. Veri kütüphaneleri ve framework'ler bunu halleder; çoğu uygulamanın promise'lerini birinden almasının nedeni budur.
use(context), bir if içinde bile
use(SomeContext), useContext(SomeContext) ile aynı değeri döndürür. Fark onu nerede çağırabildiğindedir. Diğer tüm hook'lar en üst düzeyde, her render'da aynı sırayla çalışmalıdır (hook kuralları). use erken bir return'den sonra, bir koşulun içinde ya da bir döngüde çağrılabilir.
İki dal arasında geçiş yapmak için düğmeye tıkla. use(UserContext)'i useContext(UserContext) ile değiştir; kod hook kurallarını çiğner, çünkü hook bazı render'larda çalışır, bazılarında çalışmaz.
use'un yine de sınırları vardır: onu yalnızca bir bileşeni render ederken ya da bir özel hook'un içinde çağır. Bir olay işleyicisinde ya da bir efektte context'i render sırasında zaten aldığın bir değerle oku.
Hatalar: try/catch değil, error boundary'ler
Promise reddedildiğinde hata, bir render hatası gibi en yakın error boundary'ye gider.
Post 2'yi aç; boundary hata mesajını gösterir. Post 1'i yeniden aç; çalışır: key={id} boundary'ye her gönderi için taze bir state verir, böylece eski hatayı göstermeye devam etmez.
Beklemede ya da reddedilmiş bir use'u try/catch ile yakalayamazsın. Promise beklemedeyken use özel bir değer fırlatarak render'ı keser ve etrafındaki bir catch bunu yutardı. Yakalanan değer, mesajı "Suspense Exception: This is not a real error!" ile başlayan bir hatadır ve geliştirmede React ayrıca "use was called from inside a try/catch block" kaydeder. Bir hata ekranı yerine bir yedek değer göstermek için reddedilmeyi promise'i aşağı aktarmadan önce ele al:
const postPromise = fetchPost(id).catch(() => 'This post could not be loaded.');
useEffect ile veri çekmeye kıyasla use
Klasik istemci kalıbı bir efektte veri çeker ve üç state parçası tutar:
function Profile({ id }) {
const [user, setUser] = useState(null);
const [error, setError] = useState(null);
useEffect(() => {
let ignore = false;
fetchUser(id).then(
(u) => !ignore && setUser(u),
(e) => !ignore && setError(e),
);
return () => {
ignore = true;
};
}, [id]);
if (error) return <p>Error</p>;
if (!user) return <p>Loading...</p>;
return <p>{user.name}</p>;
}
useEffect | use | |
|---|---|---|
| İstek ne zaman başlar | ilk render ekrandayken | promise oluşturulduğunda, muhtemelen bileşen render olmadan önce |
| Yükleme durumu | kendi if (!user)'ın | en yakın <Suspense> |
| Hatalar | kendi error state'in | en yakın error boundary |
| Yarış durumları | onlara karşı korursun (ignore) | bileşen her zaman kendisine verilen promise'i okur |
| Promise'i kim oluşturur | bileşen | bir ebeveyn, bir önbellek ya da bir framework |
Efekt sürümü kendi kendine yeter; framework'süz uygulamalarda hâlâ en yaygın yaklaşım olmasının nedeni budur; veri çekme sayfası onu bütünüyle anlatıyor. use, bileşenin üstündeki bir şey isteği erken başlatıp promise'i aşağı verdiğinde parlar.
Sıkça Sorulan Sorular
React'te use hook'u ne yapar?
use(resource) bir promise'in ya da bir context'in değerini döndürür. Bir promise ile bileşen promise resolve olana kadar askıya alınır ve en yakın <Suspense> yedek içeriğini gösterir; reddedilirse bunun yerine en yakın error boundary gösterilir.
use koşullu olarak çağrılabilir mi?
Evet. use, if deyimlerinin, döngülerin içinde ve erken bir return'den sonra çağırabileceğin tek hook'tur. Yine de bir bileşenden ya da bir hook'tan çağrılmalıdır, asla bir olay işleyicisinden ya da bir efektten değil.
Bileşenimin içindeki use(fetch()) neden sonsuz döngüye giriyor?
Her render yeni bir promise oluşturur. Bileşen onun üzerinde askıya alınır, promise resolve olduğunda React yeniden render eder ve o render beklemede olan başka bir promise oluşturur. Promise'i render'ın dışında oluştur: bir ebeveynin state'inde, modül düzeyinde, bir önbellekte ya da bir framework loader'ında.
use'u try/catch ile sarabilir miyim?
Hayır. Bir promise beklemedeyken use, hata fırlatarak render'ı keser ve bunu yakalamak onu bozar. Reddedilmiş bir promise'i bir error boundary ile ele al ya da promise'e bir .catch() ekle ki bir yedek değere resolve olsun.
useEffect ile veri çekmeyi use ile değiştirmeli miyim?
use, bir kez oluşturulup yeniden kullanılan bir promise'e ihtiyaç duyar; framework'lerin ve veri kütüphanelerinin sana verdiği budur. Düz bir istemci uygulamasında bir efektte (ya da TanStack Query gibi bir kütüphaneyle) veri çekmek hâlâ yaygındır ve sorun değildir.