React'ta optimistik UI güncellemeleri uygulamayı anlık hissettirebilir. Sunucu gerçeğiyle uzlaştırma, hata durumları ve veri sapmasını önleme için güvenli desenleri öğrenin.

React'ta optimistik UI, bir değişiklik sunucu tarafından onaylanmadan önce ekranı sanki değişiklik başarılıymış gibi güncellemek demektir. Birisi Beğen'e tıklar, sayaç hemen artar ve istek arka planda çalışır.
Bu anlık geri bildirim uygulamayı hızlı hissettirir. Yavaş ağlarda genellikle fark, "tepkili" ile "çalıştı mı?" arasındadır.
Takas, veri sapmasıdır: kullanıcının gördüğü zamanla sunucudaki gerçek durumla uyuşmamaya başlayabilir. Sapma genelde zamanlamaya bağlı küçük, sinir bozucu tutarsızlıklar olarak ortaya çıkar ve yeniden üretmesi zordur.
Kullanıcılar genellikle bir şeyler daha sonra "fikrini değiştirirse" fark eder: bir sayaç zıplar sonra geri döner, bir öğe görünür ve yenilemeden sonra kaybolur, bir düzenleme sayfayı tekrar ziyaret edene kadar kalır gibi görünür veya iki sekme farklı değerler gösterir.
Bunun nedeni, UI'nın bir tahminde bulunmasıdır; sunucu nihai sonucu farklı verebilir. Doğrulama kuralları, çoğaltmayı engelleme, izin kontrolleri, hız sınırları veya başka bir cihazın aynı kaydı değiştirmesi nihai sonucu etkileyebilir. Yaygın başka bir neden de çakışan isteklerdir: daha eski bir yanıt sonradan gelir ve kullanıcının daha yeni eylemini üzerine yazar.
Örnek: bir projeyi “Q1 Plan” olarak yeniden adlandırırsınız ve başlıkta hemen gösterirsiniz. Sunucu boşlukları kırpar, karakterleri reddeder veya bir slug oluşturur. Optimistik değeri sunucunun nihai değeriyle hiç değiştirmezseniz, UI sonraki yenilemeye kadar doğru görünür, sonra "gizemli" bir şekilde değişir.
Optimistik UI her zaman doğru tercih değildir. Para ve faturalama, geri döndürülemez eylemler, rol ve izin değişiklikleri, karmaşık sunucu kuralları olan iş akışları veya kullanıcının açıkça onaylaması gereken yan etkiler için temkinli olun (veya kullanmayın).
Doğru kullanıldığında optimistik güncellemeler bir uygulamayı anlık hissettirir, ancak uzlaştırma, sıralama ve hata yönetimi için plan yapmanız gerekir.
Optimistik UI, iki tür durumu ayırdığınızda en iyi şekilde çalışır:
Çoğu sapma, yerel bir tahminin onaylanmış gerçek gibi ele alınmasından başlar.
Basit bir kural: bir değer mevcut ekranın dışında iş anlamı taşıyorsa sunucu gerçeğin kaynağıdır. Sadece ekranın davranışını etkiliyorsa (açık/kapalı, odaklanmış girdi, taslak metin), yerelde tutun.
Uygulamada, izinler, fiyatlar, bakiyeler, stok, hesaplanmış veya doğrulanmış alanlar ve başka yerde değişebilecek her şey için sunucu gerçeğini tutun. Taslaklar, "düzenleniyor" bayrakları, geçici filtreler, açılmış satırlar ve animasyon geçişleri için yerel UI durumunu kullanın.
Bazı eylemler "tahmin etmek için güvenlidir" çünkü sunucu büyük olasılıkla kabul eder ve tersine çevirmesi kolaydır; örneğin bir öğeyi yıldızlamak veya basit bir tercihi açıp kapamak.
Bir alan tahmin etmek için güvenli değilse, değişikliğin kesinmiş gibi davranmak zorunda kalmadan uygulamayı hızlı hissettirebilirsiniz. Son onaylanmış değeri tutun ve net bir bekleyen sinyali ekleyin.
Örneğin, bir CRM ekranında “Ödendi olarak işaretle”e tıkladığınızda sunucu bunu reddedebilir (izinler, doğrulama, zaten iade edilmiş). Her türetilmiş sayıyı anında yeniden yazmak yerine durumu hafifçe “Kaydediliyor…” etiketiyle güncelleyin, toplamları değiştirmeyin ve yalnızca onaydan sonra toplamları güncelleyin.
İyi desenler basit ve tutarlıdır: değişen öğenin yanında küçük bir “Kaydediliyor…” rozetı, işlemi geçici olarak devre dışı bırakmak (veya onu Geri Al'a çevirmek) veya optimistik değeri geçici olarak işaretlemek (daha soluk metin veya küçük bir spinner) gibi.
Sunucu yanıtı birçok yeri etkileyebiliyorsa (toplamlar, sıralama, hesaplanmış alanlar, izinler), her şeyi yamalamaya çalışmaktansa yeniden getirmek genellikle daha güvenlidir. Küçük, izole bir değişiklik (bir notu yeniden adlandırma, bir bayrağı açma/kapama) için yerel yamalama genelde iyidir.
Kullanışlı bir kural: kullanıcının değiştirdiği tek şeyi yamalayın, sonra türetilmiş, toplanmış veya ekranlar arasında paylaşılan verileri yeniden getirin.
Optimistik UI, veriniz onaylanmış ile tahmin arasındaki farkı izlediğinde iyi çalışır. Bu boşluğu açıkça modelleyebiliyorsanız, “neden geri döndü?” anları nadirleşir.
Yeni oluşturulan öğeler için geçici bir istemci ID'si atayın (ör. temp_12345 veya bir UUID), sonra yanıt geldiğinde gerçek sunucu ID'siyle değiştirin. Bu, listelerin, seçimin ve düzenleme durumunun düzgün uzlaşmasını sağlar.
Örnek: kullanıcı bir görev ekler. Hemen id: "temp_a1" ile render edersiniz. Sunucu id: 981 ile yanıt verdiğinde ID'yi tek bir yerde değiştirirsiniz ve ID ile anahtarlanmış her şey çalışmaya devam eder.
Ekran düzeyi tek bir loading bayrağı çok kaba olur. Durumu değişen öğe (ve hatta alan) üzerinde takip edin. Böylece ince bir bekleyen UI gösterebilir, sadece başarısız olanı yeniden deneyebilir ve alakasız işlemleri engellemezsiniz.
Pratik bir öğe yapısı:
id: gerçek veya geçicistatus: pending | confirmed | failedoptimisticPatch: yerelde yaptığınız küçük değişiklikserverValue: son onaylanmış veri (veya confirmedAt zaman damgası)rollbackSnapshot: geri alabileceğiniz önceki onaylanmış değerOptimistik güncellemeler, kullanıcının gerçekten değiştirdiği şeyi dokunduğunuzda en güvenlidir (ör. completed'ı değiştirmek) yerine tüm nesneyi tahmini yeni bir sürümle değiştirmek risklidir. Bütün nesne değiştirme, daha yeni düzenlemeleri, sunucu eklediği alanları veya eşzamanlı değişiklikleri silmeyi kolaylaştırır.
İyi bir optimistik güncelleme anlık hisseder, ama sunucuyla eşleşir. Optimistik değişikliği geçici kabul edin ve onu güvenli şekilde onaylamak veya geri almak için yeterli kayıt tutun.
Örnek: bir kullanıcı listede bir görev başlığını düzenliyor. Başlık hemen güncellensin istiyorsunuz, ama doğrulama hataları ve sunucu tarafı biçimlendirmesiyle de başa çıkmanız gerekiyor.
Optimistik değişikliği yerel durumda hemen uygulayın. Geri alabilmek için küçük bir yama (veya snapshot) saklayın.
İsteği bir istek ID'si ile gönderin (artan bir sayı veya rastgele ID). Bu, yanıtları tetikleyen eyleme eşleştirmenin yoludur.
Öğeyi beklemede (pending) olarak işaretleyin. Pending UI'yı engellemek zorunda değil. Küçük bir spinner, soluk metin veya “Kaydediliyor…” olabilir. Anahtar, kullanıcının bunun henüz onaylanmadığını anlamasıdır.
Başarılı olursa, geçici istemci verisini sunucu sürümüyle değiştirin. Sunucu bir şeyi ayarladıysa (boşluk kırpma, büyük/küçük harf düzeltme, zaman damgalarını güncelleme) yerel durumu sunucuya göre güncelleyin.
Hata olursa, sadece bu isteğin değiştirdiğini geri alın ve net, yerel bir hata gösterin. Alakasız ekran parçalarını geri almaktan kaçının.
Aşağıda kütüphaneden bağımsız küçük bir örnek şekil var; kod bloğu içeriği olduğu gibi korunmuştur:
const requestId = crypto.randomUUID();
applyOptimistic({ id, title: nextTitle, pending: requestId });
try {
const serverItem = await api.updateTask({ id, title: nextTitle, requestId });
confirmSuccess({ id, requestId, serverItem });
} catch (err) {
rollback({ id, requestId });
showError("Could not save. Your change was undone.");
}
İki detay birçok hatayı önler: isteği beklerken öğe üzerinde request ID'yi saklayın ve yalnızca ID'ler eşleşiyorsa onaylayın veya geri alın. Bu, daha eski yanıtların daha yeni düzenlemelerin üzerine yazmasını durdurur.
Ağ sırası bozulduğunda optimistik UI bozulur. Klasik bir hata: kullanıcı bir başlığı düzenler, hemen sonra yine düzenler ve ilk istek sonuncu olarak biter. O geç gelen yanıt uygulanırsa UI eski bir değere geri döner.
Çözüm, her yanıtı "belki alakalı" olarak muamele etmek ve yalnızca en son kullanıcı niyetiyle eşleşiyorsa uygulamaktır.
Pratik bir desen, her optimistik değişikliğe iliştirilmiş bir istemci istek ID'si (sayacı) olmaktır. Her kayıt için en son ID'yi saklayın. Bir yanıt geldiğinde ID'leri karşılaştırın. Yanıt en yenisi değilse yoksayın.
Sunucunun döndürdüğü updatedAt, version veya etag varsa, yalnızca UI'nın zaten gösterdiğinden daha yeni olan yanıtları kabul edin.
Birlikte kullanabileceğiniz diğer seçenekler:
Örnek (istek ID koruması):
let nextId = 1;
const latestByItem = new Map();
async function saveTitle(itemId, title) {
const requestId = nextId++;
latestByItem.set(itemId, requestId);
// optimistic update
setItems(prev => prev.map(i => i.id === itemId ? { ...i, title } : i));
const res = await api.updateItem(itemId, { title, requestId });
// ignore stale response
if (latestByItem.get(itemId) !== requestId) return;
// reconcile with server truth
setItems(prev => prev.map(i => i.id === itemId ? { ...i, ...res.item } : i));
}
Kullanıcılar hızlı yazabiliyorsa (notlar, başlıklar, arama), kaydetmeyi duraklayana kadar iptal etmeyi veya kaydetme gecikmesi eklemeyi düşünün. Sunucu yükünü azaltır ve geç gelen yanıtların görünür sıçramalara neden olma olasılığını düşürür.
Hatalar, optimistik UI'nın güvenini kaybettiği yerdir. En kötü deneyim, hiçbir açıklama olmadan ani bir geri almadır. Düzenlemeler için iyi bir varsayılan: kullanıcının değerini ekranda tutun, kaydedilmedi olarak işaretleyin ve düzenlediği yerde satır içi bir hata gösterin. Bir projeyi “Alpha”dan “Q1 Launch”a yeniden adlandırdıysa, geri dönmediğiniz sürece onu “Alpha”ya geri döndürmeyin. “Q1 Launch”ı tutun, "Kaydedilmedi. İsim zaten alınmış" ekleyin ve kullanıcının düzeltmesine izin verin.
Satır içi geri bildirim, hatanın meydana geldiği tam alana bağlı kalır. Bu, bir toast'ın görünüp UI'nın sessizce geri dönmesi gibi "ne oldu şimdi?" anlarını önler.
Güvenilir ipuçları: işlem süresince “Kaydediliyor…”, hata halinde “Kaydedilmedi”, etkilenen satırın hafif vurgulanması ve kullanıcıya sonraki adımı söyleyen kısa bir mesaj.
Tekrar deneme neredeyse her zaman faydalıdır. Geri al (Undo) ise birinin pişman olabileceği hızlı eylemler (arşivleme gibi) için iyidir; ancak kullanıcı açıkça yeni değeri istiyorsa düzenlemeler için kafa karıştırıcı olabilir.
Bir mutasyon başarısız olduğunda:
Geri almak zorundaysanız (ör. izinler değişti ve kullanıcı artık düzenleyemiyor), bunu açıklayın ve sunucu gerçeğini geri yükleyin: "Kaydedilemedi. Artık düzenleme izniniz yok." gibi.
Sunucu yanıtını sadece bir başarı bayrağı değil, bir fiş (receipt) gibi düşünün. İstek tamamlandığında uzlaştırın: kullanıcının niyetini koruyun ve sunucunun daha iyi bildiği şeyleri kabul edin.
Sunucu, yerel tahmininizden daha fazlasını değiştirmiş olabilir; bu durumda tam yeniden getirme en güvenli yoldur ve mantığı daha kolaydır.
Mutasyon birçok kaydı etkiliyorsa (öğeleri bir listeden diğerine taşıma), izinler veya iş akışı kuralları sonucu etkileyebiliyorsa, sunucu kısmi veri döndürüyorsa veya diğer istemciler aynı görünümü sık güncelliyorsa yeniden getirme daha iyidir.
Sunucu güncellenmiş varlığı (veya yeterince alanı) döndürüyorsa, birleştirme daha iyi bir deneyim olabilir: UI stabil kalır ama yine de sunucu gerçeğini kabul eder.
Sapma genellikle optimistik bir nesne ile sunucuya ait alanları ezmekten gelir. Sayaçlar, hesaplanmış değerler, zaman damgaları ve normalleştirilmiş biçimler buna örnektir.
Örnek: optimistik olarak likedByMe=true ve likeCount artırdınız. Sunucu çift beğeniyi dedupe edebilir ve farklı bir likeCount döndürebilir, ayrıca updatedAt güncellenmiş olabilir.
Basit bir birleştirme yaklaşımı:
Çatışma olduğunda önceden karar verin. "Son yazma kazanır" toggles için uygundur. Formlar için alan düzeyinde birleştirme daha iyidir.
Bir alan için "istekten beri kirli" (dirty since request) bayrağı veya yerel sürüm numarası tutmak, mutasyon başladıktan sonra kullanıcı tarafından değiştirilen alanlar için sunucu değerlerini göz ardı etmenizi sağlayıp diğer her şeyi kabul etmenize izin verir.
Sunucu mutasyonu reddederse, sürpriz bir geri almadan ziyade spesifik, hafif hataları tercih edin. Kullanıcının girdisini tutun, alanı vurgulayın ve mesajı gösterin. Geri alma, yalnızca eylem gerçekten dayanamaz durumda olduğunda kullanılmalı (ör. optimistik olarak sildiğiniz bir öğeyi sunucu silmeyi reddettiyse).
Listeler, optimistik UI'nın harika hissettirdiği ama kolayca bozulduğu yerlerdir. Bir öğenin değişmesi sıralamayı, toplamları, filtreleri ve birden fazla sayfayı etkileyebilir.
Oluşturmalarda, yeni öğeyi hemen gösterin ama beklemede olarak işaretleyin ve geçici bir ID kullanın. Pozisyonunu stabil tutun ki zıplamasın.
Silmelerde, öğeyi hemen gizlemek güvenli bir desendir ama sunucu onaylayana kadar kısa süreli “hayalet” bir kayıt hafızada tutun. Bu, geri alma desteği sağlar ve hataları yönetmeyi kolaylaştırır.
Yeniden sıralama birçok öğeyi etkilediği için zordur. Optimistik olarak yeniden sıralama yapıyorsanız, gerekiyorsa geri alabilmek için önceki sıralamayı saklayın.
Sayfalama veya infinite scroll ile, optimistik eklemelerin nereye ait olacağını karar verin. Akışlarda yeni öğeler genelde en üste gider. Sunucu sıralamasına göre sıralanan kataloglarda, yerel ekleme yanıltıcı olabilir çünkü sunucu öğeyi başka bir yere koyabilir. Pratik bir uzlaşma, görünür listeye ekleyip beklemede rozeti koymak ve sunucu yanıtında son sıralama anahtarı farklıysa taşımaya hazır olmaktır.
Geçici ID gerçek ID olduğunda, karışmaları önlemek için kararlı bir anahtarla eşleyin. Sadece ID ile eşleştirirseniz aynı öğeyi iki kez (temp ve onaylanmış) gösterebilirsiniz. Bir tempId->realId eşlemesi tutun ve yerinde değiştirin ki kaydırma pozisyonu ve seçim sıfırlanmasın.
Sayım ve filtreler de liste durumudur. Sayaçları yalnızca sunucunun büyük olasılıkla aynı şeyi kabul edeceğine güveniyorsanız optimistik olarak güncelleyin. Aksi halde onları yenileniyor (refreshing) olarak işaretleyin ve yanıt sonrası uzlaştırın.
Çoğu optimistik-güncelleme hatası gerçekten React'la ilgili değildir. Bunlar optimistik bir değişikliği "yeni gerçek" olarak ele almaktan kaynaklanır; oysa o sadece geçici bir tahmindir.
Sadece bir alan değiştiğinde tüm nesneyi veya ekranı optimistik olarak güncellemek etki alanını gereksizce genişletir. Sonraki sunucu düzeltmeleri alakasız düzenlemeleri geri yazabilir.
Örnek: bir profil formu bir ayarı değiştirirken tüm user nesnesini değiştiriyor. İstek uçuşta iken kullanıcı ismini düzenlerse, yanıt gelince eski isim geri gelebilir.
Optimistik yamaları küçük ve odaklı tutun.
Başka bir sapma kaynağı, başarı veya hata sonrası bekleyen bayrakları temizlemeyi unutmaktır. UI yarım yüklenmiş kalır ve sonraki mantık onu hala optimistik zanneder.
Öğe başına bekleyen durumu takip ediyorsanız, aynı anahtarı kullanarak temizleyin. Geçici ID'ler gerçek ID'ye doğru her yerde eşlenmediğinde "hayalet bekleyen" öğeler oluşur.
Geri alma hataları, snapshot'ın çok geç saklanması veya çok geniş kapsamlı olması durumunda olur.
Kullanıcı iki hızlı düzenleme yaparsa, #2'yi geri almak yerine #1 öncesi snapshot'ı kullanarak #2'yi bozabilirsiniz. UI kullanıcının hiç görmediği bir duruma atlar.
Çözüm: geri alacağınız kesin parçayı anında snapshot'layın ve onu belirli bir mutasyon denemesine bağlayın (genellikle request ID ile).
Gerçek kaydetmeler genellikle çok adımlıdır. 2. adım başarısız olursa (ör. resim yüklemesi), 1. adımı sessizce geri almayın. Ne kaydedildi, ne kaydedilmedi, kullanıcının ne yapabileceğini gösterin.
Ayrıca sunucunun tam olarak gönderdiğinizi yankılayacağını varsaymayın. Sunucular metni normalleştirir, izinleri uygular, zaman damgası koyar, ID atar ve alanları düşürebilir. Optimistik yamayı sonsuza dek güvenilir kabul etmek yerine her zaman yanıttan uzlaştırın (veya yeniden getirin).
Optimistik UI öngörülebilir olduğunda işe yarar. Her optimistik değişikliği mini bir işlem gibi ele alın: bir ID'si var, görünür bir bekleyen durumu var, net bir başarı değişimi var ve kullanıcıları şaşırtmayacak bir hata yolu var.
Göndermeden önce kontrol listesi:
Hızlı prototipleme yapıyorsanız ilk sürümü küçük tutun: bir ekran, bir mutasyon, bir liste güncellemesi. Araçlar gibi Koder.ai (koder.ai) UI ve API'yi daha hızlı taslağa almanıza yardımcı olabilir, ama aynı kural geçerli: istemci hiçbir zaman sunucunun gerçekten kabul ettiklerini takip edebilsin diye bekleyen vs onaylanmış durumu modelleyin.
Optimistik UI, ekranı değişiklik sunucu tarafından doğrulanmadan hemen günceller. Uygulamayı anlık hissettirir, fakat UI'nın gerçek kaydedilmiş durumdan sapmaması için yine de sunucu yanıtıyla uzlaştırma yapmanız gerekir.
Veri sapması, UI'nın optimistik tahmini onaylanmış gibi kalıp sunucunun farklı bir şey kaydetmesi veya reddetmesi durumunda ortaya çıkar. Genellikle yenileme sonrası, başka bir sekmede veya yavaş ağlarda cevapların sırası bozulduğunda görülür.
Para, faturalama, geri döndürülemez işlemler, izin değişiklikleri ve sunucunun sıkı kuralları olan iş akışlarında optimistik güncellemelerden kaçının veya çok temkinli olun. Bu durumlarda daha güvenli olan, net bir bekleyen durumu göstermek ve onay beklemektir.
Ekranın dışında iş anlamı olan her şey için backend gerçek kaynaktır: fiyatlar, izinler, hesap bakiyeleri, stoklar ve ortak sayaçlar. Taslaklar, odak, "düzenleniyor" bayrakları ve sadece sunumla ilgili durumlar yerelde tutulmalıdır.
Değişikliğin olduğu yerde küçük, tutarlı bir sinyal gösterin: “Kaydediliyor…”, soluk metin veya ince bir spinner gibi. Amaç değerin geçici olduğunu engellemeden belli etmek.
Yeni oluşturulan öğeler için bir geçici istemci ID'si (UUID veya temp_...) kullanın, sonra sunucu gerçek ID'yi döndüğünde değiştirin. Bu liste anahtarlarının, seçim ve düzenleme durumunun sabit kalmasını sağlar.
Bir küresel yükleme bayrağı kullanmayın; bekleyen durumu her öğe (veya alan) için izleyin. Küçük bir optimistik yama ve geri alma için snapshot saklayın ki sadece değişen şey onaylansın ya da geri alınsın.
Her mutasyona isteğe özgü bir ID (request ID) ekleyin ve öğe başına en son request ID'yi saklayın. Bir yanıt geldiğinde, eğer ID en son olanla eşleşmiyorsa onu yoksayın; böylece geç kalan yanıtlar UI'yı eski bir değere geri döndüremez.
Çoğu düzenleme için kullanıcının değerini ekranda tutun, "kaydedilmedi" olarak işaretleyin ve düzenlediği yerde satır içi bir hata gösterip tekrar deneme seçeneği verin. Sadece gerçekten imkânsızsa (ör. izinler kaybolduysa) sert bir geri alma yapın ve nedeni açıklayın.
Değişiklik birçok yeri etkileyebilecekse (toplamlar, sıralama, izinler veya türetilmiş alanlar), yeniden getirmek (refetch) genellikle daha güvenlidir. Küçük, izole bir güncelleme ise ve sunucu güncellenmiş nesneyi döndürdüyse yerel birleştirme (merge) daha iyi bir deneyim verir.