useSyncExternalStore


  • Store nasıl çevrilecek?

Referans

useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot?)

Harici veri deposundan değer okumak için bileşeninizin en üst kapsamında useSyncExternalStore’u çağırın.

Depodaki verinin anlık görüntüsünü döndürür. Argüman olarak iki fonksiyon geçmeniz gerekir:

  1. subscribe fonksiyonu, depoya (data store) abone olmalı (subscribe) ve abonelikten çıkmak için fonksiyon döndürmelidir.
  2. getSnapshot fonksiyonu, depodaki verinin anlık görüntüsünü okumalıdır.

Parametreler

  • subscribe: Bir callback argümanı alan ve depoya abone olan fonksiyondur. Depo değiştiğinde, iletilen callback çalıştırılır. Bu, bileşeni yeniden render eder ve (ihtiyac varsa) getSnapshot i yeniden cagirir. subscribe fonksiyonu, aboneliği temizleyen bir fonksiyon döndürmelidir.

  • getSnapshot: Bileşenin ihtiyaç duyduğu depodaki verilerin anlık görüntüsünü döndüren fonksiyondur. Veri deposu değişmemişse, getSnapshot’a yapılan çağrılar aynı değeri döndürmelidir. Depo değişirse ve döndürülen değer farklıysa ( ile karşılaştırıldığında), bileşen yeniden render edilir.

  • isteğe bağlı getServerSnapshot: Depodaki verilerin başlangıçtaki anlık görüntüsünü döndüren fonksiyondur. Yalnızca sunucu taraflı render ya da istemcide render edilmiş çıktının hidratlanması sırasında çalıştırılır. Serileştirilerek sunucudan istemciye iletilen sunucu anlık görüntüsü, istemci ile aynı olmalıdır. Bu argümanı iletirseniz, bileşen sunucu tarafında render edilirken hata fırlatır.

Dönüş değeri

Render mantığınızda kullanabileceğiniz deponun o anki anlık görüntüsüdür.

Dikkat edilmesi gerekenler

  • getSnapshot tarafından döndürülen depo anlık görüntüsü değiştirilemez (immutable) olmalıdır. Depoda değiştirilebilir veri varsa veriler değiştiğinde yeni bir anlık görüntü döndürün. Aksi takdirde, önbelleğe alınmış en son anlık görüntüyü döndürün.

  • Yeniden render esnasında farklı bir subscribe fonksiyonu geçildiğinde React, yeni geçilen subscribe fonksiyonu ile depoya yeniden abone olur. subscribe’ı bileşenin dışında tanımlayarak bunu önleyebilirsiniz.

  • If the store is mutated during a , React will fall back to performing that update as blocking. Specifically, for every Transition update, React will call getSnapshot a second time just before applying changes to the DOM. If it returns a different value than when it was called originally, React will restart the update from scratch, this time applying it as a blocking update, to ensure that every component on screen is reflecting the same version of the store.

  • It’s not recommended to suspend a render based on a store value returned by useSyncExternalStore. The reason is that mutations to the external store cannot be marked as , so they will trigger the nearest , replacing already-rendered content on screen with a loading spinner, which typically makes a poor UX.

    For example, the following are discouraged:


Kullanım

Harici depoya abone olma

React bileşenlerinizin çoğu veriyi yalnızca , ve ’den okur. Ancak bileşenler, bazı verileri React dışındaki bir depodan (store) okuma ihtiyacı duyabilir. Aşağıdaki durumlar buna örnektir:

  • React dışında state tutan üçüncü parti state yönetim kütüphaneleri.
  • Değiştirebilir değer ve değişikliklere abone olmak için olaylar (event) sunan tarayıcı API’leri.

Harici veri deposundan bir değer okumak için bileşeninizin en üst kapsamında useSyncExternalStore’u çağırın.

Veri deposundaki verilerin anlık görüntüsünü döndürür. Argüman olarak iki fonksiyon geçmeniz gerekir:

  1. subscribe fonksiyonu, depoya abone olmalı ve aboneliği sonlandıran fonksiyon döndürmelidir.
  2. getSnapshot fonksiyonu, depodan veriyi anlık görüntüsünü okumalıdır.

React, bu fonksiyonları kullanarak bileşeninizi depoya abone tutar ve değişikliklerde yeniden render eder.

Aşağıdaki örnekte todosStore, React’ın dışında veri tutan harici bir depo olacak şekilde implemente edilmiştir. TodosApp bileşeni useSyncExternalStore Hook’u ile harici depo ile bağlantı kurar.


Tarayıcı API’sine abone olma

useSyncExternalStore kullanmak için başka bir neden, tarayıcı tarafından sunulan ve zamanla değişen değerlere abone olmaktır. Örneğin, bileşeninizde ağ bağlantısının etkin olup olmadığını göstermek istiyorsunuzdur. Tarayıcı, bu bilgiyi özelliği aracılığıyla sunar.

Bu değer React’ın bilgisi dışında değişebilir ve bu sebeple useSyncExternalStore ile okumanız gerekir.

getSnapshot fonksiyonunu implemente etmek için tarayıcı API’sinden geçerli değeri okuyun:

Ardından, subscribe fonksiyonunu implemente etmeniz gerekir. Örneğin, navigator.onLine değiştiğinde window nesnesi üzerinden ve olayları tetiklenir. callback argümanıyla bu olaylara abone olmanız ve abonelikleri temizleyen bir fonksiyon döndürmeniz gerekir.

Artık React, harici navigator.onLine API’sinin değerini nasıl okuyacağını ve değişikliklere nasıl abone olacağını bilir. Cihazınızın ağ bağlantısı kesin ve bileşenin buna karşılık yeniden render’ı tetiklediğine dikkat edin:


Mantığı özel bir hook’a çıkarma

Usually you won’t write useSyncExternalStore directly in your components. Instead, you’ll typically call it from your own custom Hook. This lets you use the same external store from different components.

For example, this custom useOnlineStatus Hook tracks whether the network is online:

Genellikle useSyncExternalStore’u bileşenlerinizde doğrudan kullanmazsınız. Bunun yerine kendi özel Hook’unuzda çağırırsınız. Böylece aynı harici depoyu farklı bileşenlerden de kullanabilirsiniz.

Örneğin, örnekteki özel useOnlineStatus Hook’u ağın çevrimiçi olup olmadığını takip eder:

Artık farklı bileşenler, implementasyonu sürekli tekrarlamadan useOnlineStatus çağırabilir:


Sunucu taraflı render desteği ekleme

React uygulamanız kullanıyorsa, React bileşenleriniz başlangıç HTML’ini üretmek için tarayıcı ortamının dışında da çalışacaktır. Bu durum, harici depoya bağlanırken bazı zorlukları beraberinde getirir:

  • Yalnızca tarayıcıda bulunan bir API’ye bağlanıyorsanız, çalışmayacaktır çünkü sunucuda mevcut değildir.
  • Üçüncü taraf bir veri deposuna bağlanıyorsanız, sunucu ve istemci arasında verilerin eşleşmesi gerekmektedir.

Bu sorunları çözmek için, useSyncExternalStore’a üçüncü argüman olarak getServerSnapshot fonksiyonunu iletin:

getServerSnapshot fonksiyonu getSnapshot’a benzer ancak yalnızca iki durumda çalışır:

  • HTML oluşturulurken sunucuda çalışır.
  • React’ın sunucu HTML’ini alıp etkileşimli haline getirirken yani yaparken istemcide çalışır.

Bu durum, uygulama etkileşimli hale gelmeden önce kullanılacak olan başlangıç anlık görüntü değeri vermenizi sağlar. Sunucu taraflı render için anlamlı bir başlangıç değeriniz yoksa, için bu argümanı atlayın.


Sorun giderme

“The result of getSnapshot should be cached” hatası alıyorum

Bu hata, getSnapshot fonksiyonunuzun her çağırıldığında yeni bir nesne döndürdüğü anlamına gelir, örneğin:

getSnapshot son seferkinden farklı bir değer döndürdüğünde, React bileşeni yeniden render eder. Dolayısıyla her seferinde farklı sonuç döndürdüğünüzde sonsuz döngüye girer ve hata alırsınız.

getSnapshot nesneniz yalnızca gerçekten değiştiğinde farklı bir nesne döndürür. Deponuz değişmez (immutable) veri içeriyorsa, bu verileri doğrudan döndürebilirsiniz:

Deponuzdaki veri değişken (mutable) ise getSnapshot fonksiyonunuz değişmez anlık görüntüsünü döndürmelidir. Yani her çağrıldığında farklı nesne oluşturması gerektiği anlamına gelir. Bunun yerine, son hesaplanan anlık görüntüyü depolamalı ve depodaki veri değişmediyse bir önceki anlık görüntüyü döndürmelidir. Değişken verilerin değişip değişmediğini nasıl belirleyeceğiniz deponuza bağlıdır.


subscribe fonksiyonum her render’dan sonra çağırılıyor

Örnekteki subscribe fonksiyonu bileşenin içinde tanımlanmıştır ve bu nedenle her render’da farklıdır:

Yeniden render’lar arasında farklı bir subscribe fonksiyonu iletirseniz, React deponuza yeniden abone olur. Bu durum performans sorunlarına neden oluyorsa ve sürekli abone olmaktan kaçınmak istiyorsanız, subscribe fonksiyonunu bileşen dışına taşıyın:

Alternatif olarak, yalnızca bir takım argümanlar değiştiğinde yeniden abone olmak için subscribe fonksiyonunu Hook’una sarın: