useOptimistic, ekranın bir action'ın sonucunu action bitmeden göstermesini sağlayan bir React 19 hook'udur. Bir action içinde iyimser bir değer ayarlarsın; React action beklemedeyken onu gösterir, sonra gerçek state'e geri döner. İstek başarısız olduysa gerçek state hiç değişmemiştir, bu yüzden ekran kendi kendine geri alınır.
Bu örnekteki sahte API gerçek bir isteğin yerine geçer ve her üçüncü çağrıda başarısız olur, böylece iki sonucu da görebilirsin.
Tıklamalar arasında bir saniye bekleyerek düğmeye üç kez tıkla. İlk ikisi hemen değişir ve kaydedildi satırı bir saniye sonra yetişir. Üçüncüsü de hemen değişir, sonra istek başarısız olduğunda geri döner. calls % 3'ü calls % 2 yap; her ikinci tıklama başarısız olur.
Sözdizimi
const [optimisticState, setOptimistic] = useOptimistic(state, updateFn?);
stategerçek değerdir, genellikleuseState'ten, props'tan ya dauseActionState'ten. Bekleyen bir action olmadığındaoptimisticStatetam olarak bu değerdir.setOptimistic(value)iyimser değeri mevcut action çalıştığı sürece ayarlar.updateFnisteğe bağlıdır:(currentState, optimisticValue) => nextState. OnunlasetOptimistictamamen yeni bir değer yerine bir değişiklik alır. Aşağıdaki reducer biçimine bak.
Bir tıklamanın yaşam döngüsü her zaman aynıdır:
- Bir action içinde
setOptimisticçağırırsın. Ekran hemen güncellenir. - Action isteği bekler.
- Başarıda gerçek state'i güncellersin (bir
await'ten sonra geliyorsastartTransitioniçinde). - Action biter.
optimisticStateyenidenstate'i takip eder: başarıda kaydedilen değer, başarısızlıkta eski değer.
Örnekte geri alma kodunun olmamasının nedeni 4. adımdır. catch bloğu yalnızca bir hata mesajı ayarlar.
Bir action içinde çalışmalı
İyimser değer yalnızca bir transition beklemedeyken var olur, bu yüzden setOptimistic'in bir transition içinde çağrılması gerekir. Bunların hepsi sayılır:
startTransition'a (ya dauseTransition'dan gelenstartTransition'a) aktardığın fonksiyon,<form action={...}>'a ya da<button formAction={...}>'a aktarılan bir fonksiyon,- useActionState'e verdiğin action.
Düz bir onClick'ten çağrıldığında iyimser değerin içinde yaşayacağı bir action yoktur. React onu hemen geri alır ve geliştirmede React "An optimistic state update occurred outside a transition or action" kaydeder.
Bir await'ten sonra olan gerçek state güncellemeleri, yukarıdaki startTransition(() => setLiked(next)) gibi kendi startTransition'larıyla sarılmalıdır. React bir await'ten sonra hâlâ önceki transition'ın içinde olduğunu anlayamaz.
Reducer biçimiyle bir mesaj listesi
İyimser state bir liste olduğunda ikinci argüman olarak bir güncelleme fonksiyonu aktar. Setter o zaman yeni öğeyi alır ve React onu o anda gerçek liste ne ise ona ekler.
Farklı metinlerle üç mesajı teker teker gönder. Her biri "(sending...)" ile soluk görünür ve teslim edildiğinde koyulaşır; üçüncüsü sohbetten kaybolur ve "Not sent" olarak görünür. Bunun yerine onları hızlıca gönder; üçü de son istek bitene kadar soluk kalır, çünkü React iyimser değerleri bekleyen her action bitene kadar tutar. Form action'ı zaten bir transition'dır, bu yüzden addOptimistic'in burada kendi startTransition'ına ihtiyacı yoktur.
Her iyimser öğeye gerçek öğelerle çakışmayacak bir key ver. Örnek pending- artı metni kullanır, bu yüzden ikisi beklemedeyken aynı metni iki kez göndermek çakışırdı; gerçek bir uygulama istemcide bir id oluştururdu.
Aynı anda birkaç güncelleme
Güncelleme fonksiyonu mevcut state'i aldığı için iyimser güncellemeler üst üste biner. Hızlıca tıkla; bekleyen her tıklama bir öncekinin üstüne uygulanır.
Hızlıca dört kez tıkla. Sepet hemen 4 gösterir. İstekler bittiğinde sayı 3'te sabitlenir, çünkü üçüncü istek başarısız oldu ve konsol bunu söyler. React iyimser değerleri bekleyen her action bitene kadar tutar, sonra gerçek sayıyı gösterir.
Ne zaman kullanılmalı, ne zaman değil
İyimser güncellemeler neredeyse her zaman başarılı olan ve geri alması kolay action'lara uyar: beğeniler, yıldızlar, açma kapamalar, yeniden adlandırma, bir mesaj eklemek. Kullanıcı yaygın durum için hiç spinner görmez.
Yanlış bir "tamam"ın yanıltacağı yerlerde onlardan kaçın: ödemeler, bir hesabı silmek, kullanıcının sonuç bilinmeden önce harekete geçebileceği her şey. Bunlar için useActionState'ten ya da useTransition'dan gelen isPending ile bir bekleme durumu göster ve gerçek cevabı bekle.
Bir geri alma olduğunda bunu her zaman kullanıcıya söyle. Sessizce geri değişen bir değer bir hata gibi görünür. Yukarıdaki iki örnek de action'dan uzun yaşaması için bir hata mesajını sıradan state'te tutar.
useActionState ile
useOptimistic ve useActionState birbirine uyar. useActionState'ten gelen action zaten bir transition'dır, bu yüzden iyimser değeri onun başında ayarlayabilirsin ve döndürülen state, iyimser değerin geri döndüğü gerçek değerdir.
const [state, formAction] = useActionState(async (previous, formData) => {
const title = formData.get('title');
setOptimisticTitle(title);
const saved = await saveTitle(title);
return { title: saved.title };
}, { title: 'Untitled' });
const [optimisticTitle, setOptimisticTitle] = useOptimistic(state.title);
Kaydetme çalışırken sayfa yeni başlığı gösterir. Resolve olduğunda state.title kaydedilen değeri tutar. saveTitle hata fırlatırsa action'ın hatası en yakın error boundary'ye gider; bunun yerine eski başlığı bir mesajla göstermek için hatayı action içinde yakala ve önceki state'i artı bir hatayı döndür.
Yaygın hatalar
Setter'ı bir action'ın dışında çağırmak. İyimser değer ekranda kalmaz ve React geliştirmede uyarır. Onu startTransition ile sar ya da bir form action'ına taşı.
Gerçek state'i güncellemeyi unutmak. Başarıda action, useOptimistic'in yansıttığı state'i (setLiked, setMessages) değiştirmelidir. Aksi hâlde iyimser değer action bittiğinde kaybolur ve başarılı bir istek bir geri alma gibi görünür.
Bir await'ten sonra gerçek state'i transition olmadan güncellemek. Güncelleme yine gerçekleşir, ama React onu action'ın sonundan farklı bir anda gösterebilir. Üç örneğin de yaptığı gibi onu startTransition ile sar.
İyimser değeri doğruluk kaynağı olarak kullanmak. İstekleri gönder ve toplamları gerçek state'ten hesapla. İyimser değer yalnızca gösterim içindir ve her an atılabilir.
Sıkça Sorulan Sorular
useOptimistic ne yapar?
Sana async bir action çalışırken hemen değiştirebileceğin bir state parçasının kopyasını verir. Action bittiğinde kopya yeniden gerçek state'i takip etmeye başlar; o noktada gerçek state ya kaydedilen sonucu ya da eski değeri tutar.
useOptimistic hatada nasıl geri alır?
Özel bir hata yoluna ihtiyacı yoktur. İyimser değer yalnızca action beklemedeyken yaşar. İstek başarısız olur ve gerçek state'i hiç güncellemezsen hook gerçek state'i, yani tıklamadan önceki değeri yeniden gösterir.
Neden "An optimistic state update occurred outside a transition or action" uyarısı alıyorum?
Setter düz bir olay işleyicisinden çağrıldı. Onu startTransition'a aktarılan bir fonksiyonun içinde, bir <form action> içinde ya da useActionState'ten gelen bir action içinde çağır.
useOptimistic'in ikinci argümanı nedir?
İsteğe bağlı bir güncelleme fonksiyonu: (currentState, optimisticValue) => newState, bir reducer gibi. Onunla setter yalnızca değişikliği (yeni bir mesaj, +1) alır ve React, aynı anda birkaç güncelleme beklemede olsa bile iyimser state'i hesaplar.
useOptimistic yalnızca formlar için mi?
Hayır. Herhangi bir transition içinde çalışır, bu yüzden startTransition(async () => { ... }) çağıran bir düğme de bir form action'ı kadar onu kullanabilir.