useSyncExternalStore, bir bileşenin eşzamanlı render işleminin (concurrent rendering) neden olabileceği görsel hatalara yol açmadan, React dışında yaşayan verileri okumasını sağlar. Harici değerleri useEffect kullanarak yerel state'e çekme şeklindeki eski yöntemin yerini alır; bu eski yöntem, "tearing" (yırtılma) adı verilen, UI'ın bazı kısımlarının aynı render sırasında farklı değerler göstermesi durumuna neden olabilir.
Eski yöntemin eşzamanlılık (concurrency) altında neden bozulduğu
React 18'den önce yaygın yöntem şuydu:
const [value, setValue] = useState(initial);
useEffect(() => {
const unsubscribe = externalSource.subscribe(v => setValue(v));
return unsubscribe;
}, []);
Effect, bileşen render edildikten sonra çalışır. React'in eşzamanlı render motoru (concurrent renderer) bir render işlemini duraklatmaya, iptal etmeye veya yeniden oynatmaya karar verdiğinde, effect, state'i okuyan UI'dan farklı bir noktada tetiklenebilir. Sonuç bir uyumsuzluktur: bir bileşen eski değeri okumuş olabilir, diğeri ise yeni değeri; bu da ekranın kısa süreliğine tutarsız veriler göstermesine neden olur. Bu "tearing" durumu tamamen senkron bir dünyada zararsızdır, ancak React UI'ın birden fazla versiyonunu paralel olarak render ettiğinde görünür bir hataya dönüşür.
useSyncExternalStore sorunu nasıl çözer
Hook iki callback gerektirir:
- subscribe – bir dinleyici kaydeder ve onu kaldıran bir fonksiyon döndürür.
- getSnapshot – harici kaynağın mevcut değerini döndürür.
React, bileşen mount olduğunda subscribe'ı çağırır ve unmount olduğunda onu temizler. Her render sırasında getSnapshot'ı çağırır ve aynı harici store'u kullanan tüm bileşenlerin tam olarak aynı snapshot'ı okumasını garanti eder. Okuma işlemi render aşamasında gerçekleştiği için render motoru, tüm ağaç genelindeki güncellemeleri koordine edebilir ve tearing sorununu ortadan kaldırır.
Zustand ve Redux gibi kütüphaneler store'larını halihazırda bu hook ile sarmalamıştır; bu nedenle React 18'in eşzamanlılık özellikleriyle doğrudan uyumlu çalışırlar.
Hook'un hatalı çalışmasını önleyen kurallar
- Sadece bileşenin ihtiyacı olanı döndürün – büyük bir nesne, React'i her güncellemede derin yapıları karşılaştırmaya zorlar. Dar bir snapshot, değişiklik tespitini düşük maliyetli tutar.
- Referans kimliğini koruyun – eğer harici değer değişmediyse, getSnapshot aynı nesne referansını döndürmelidir. Her çağrıda yeni bir nesne döndürmek, React her render'da bir değişiklik gördüğü için sonsuz bir render döngüsünü tetikler.
- Kullanımı harici kaynaklarla sınırlayın – yerel bileşen state'i
useStateveyauseReduceriçinde olmalıdır. Harici store hook'unu tamamen dahili değerler için kullanmak, gereksiz bir dolaylılık ekler.
Ne zaman useSyncExternalStore kullanılmalı
- Tarayıcı API'larını sarmalamak (pencere boyutu, ağ çevrimiçi durumu, medya sorguları).
- React'ten haberi olmayan düz bir JavaScript store'unu bağlamak.
- Prop-drilling yerine, birçok bileşenin abone olduğu küresel, salt okunur bir kaynak kullanmak.
Karşı görüş
useState + useEffect deseni, özellikle bir uygulama eşzamanlılık özelliklerini etkinleştirmediğinde, basit ve seyrek değişen veriler için hala çalışır. Abonelik mantığı açıkça effect içinde bulunduğu için bazı geliştiricilerin üzerinde düşünmeyi daha kolay bulduğu, daha düşük seviyeli bir yaklaşımdır. Ancak bunun bedeli, eşzamanlı render açıldığında tearing riskiyle karşı karşıya kalmaktır. React 18'in yeni yeteneklerini halihazırda kullanan projeler için, useSyncExternalStore'un sağladığı ekstra güvenlik, eklenen minimal boilerplate kodundan daha ağır basar.
Sonrasında nelere dikkat edilmeli
- Store'unuzun getSnapshot fonksiyonunun kararlı bir referans döndürdüğünü doğrulayın; geliştirme sırasında referansın hızlı bir
console.logile kontrol edilmesi, kazara nesne yeniden oluşturulmasını ortaya çıkarabilir. - Abonelik fonksiyonunu hafif tutun – ağır işler React'e iletilen callback içinde değil, harici kaynağın içinde gerçekleşmelidir.
- Herhangi bir tearing oluşmadığından emin olmak için bileşenleri React'in eşzamanlı modunda (örneğin,
ReactDOM.createRootile birlikteactkullanarak) test edin.
Canlı bir demo, hook'un nasıl çalıştığını; React dışında saklanan basit bir sayacı senkronize ederek ve görsel hatalar olmadan birden fazla bileşeni güncelleyerek gösteriyor. Demoya https://usesyncexternalstore.vercel.app/ adresinden ulaşılabilir. Kaynak kod GitHub'da bulunuyor ve daha uzun bir yazı uygulama detaylarını açıklıyor.
Özet: React'in UI parçalarını sırasız bir şekilde render edebildiği bir dünyada, useSyncExternalStore harici veriler ile bileşen render işlemleri arasındaki güvenilir köprüdür. Görsel tutarlılığı korur, küresel store'lara ölçeklenebilir ve popüler state-management kütüphanelerine halihazırda entegre edilmiştir. React dışı state'leri okumanız gereken her yerde onu kullanın ve tüm faydalarını elde etmek için snapshot'ları küçük ve kararlı tutun.
