Metance Logo

Nuggets

useSyncExternalStore

useSyncExternalStore, harici veri depolarına (store) abone olmanızı sağlayan React Hook’udur.



  • 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.

Daha fazla örnek için aşağıya bakın.

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 (Object.is 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 non-blocking Transition update, 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 non-blocking Transition updates, so they will trigger the nearest Suspense fallback, replacing already-rendered content on screen with a loading spinner, which typically makes a poor UX.For example, the following are discouraged:const LazyProductDetailPage = lazy(() => import('./ProductDetailPage.js')); function ShoppingApp() { const selectedProductId = useSyncExternalStore(...); //  Calling `use` with a Promise dependent on `selectedProductId` const data = use(fetchItem(selectedProductId)) //  Conditionally rendering a lazy component based on `selectedProductId` return selectedProductId != null ? <LazyProductDetailPage /> : <FeaturedProducts />; }


Kullanım 

Harici depoya abone olma 

React bileşenlerinizin çoğu veriyi yalnızca prop, state ve context’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.

Not

Mümkün mertebe useState ve useReducer aracılığıyla yerleşik React state’ini kullanmanızı öneririz. useSyncExternalStore API’si, bileşenlerinizi React olmayan kodlarınızla entegre etmeniz gerektiğinde kullanışlıdır.



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 navigator.onLine ö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 online ve offline 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 sunucu taraflı render’lama 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 hidratlama 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, istemcide render işlemini zorlamak için bu argümanı atlayın.

Not

getServerSnapshot’ın istemci tarafındaki ilk render’da sahip olduğu verilerin, sunucudan döndürdüğü verilerle birebir aynı olduğundan emin olun. Örneğin getServerSnapshot sunucuda doldurulmuş olarak gelen depo içeriği döndürdüyse, bu içeriği istemciye aktarmanız gerekir. Bunun yapmanın bir yolu, sunucu taraflı render esnasında window.MY_STORE_DATA gibi bir global tanımlayan <script> etiketi kullanmak ve ardından istemcide getServerSnapshot içinden bu global değişkeni okumaktır. Harici deponuz bunu nasıl yapacağınıza ilişkin talimatlar sağlamalıdır.



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 useCallback Hook’una sarın:

© Copyright Rementis Inc. All Rights Reserved