React 18 ने useSyncExternalStore जोड़ा है, जो एक ऐसा hook है जो components को React tree के बाहर रहने वाले किसी भी source—जैसे browser APIs, WebSockets, या third-party stores—से डेटा पढ़ने की अनुमति देता है, बिना उन visual glitches के जो useState और useEffect के साथ डेटा को mirror करने के पुराने pattern में आते थे। यदि आपने कभी UI को flicker होते देखा है क्योंकि उसका एक हिस्सा पुराने window size को दिखा रहा था जबकि दूसरा हिस्सा पहले से ही नए size को दिखा रहा था, तो यह hook वही समाधान है जिसका आप इंतज़ार कर रहे थे।

external state को एक नए hook की आवश्यकता क्यों है

React components हमेशा से उन चीज़ों को subscribe करने में सक्षम रहे हैं जो उनके अपने scope के बाहर मौजूद होती हैं: जैसे window object, Redux store, या WebSocket connection। पारंपरिक तरीका useEffect के अंदर एक subscription सेट करना, setState के माध्यम से आने वाली values को local state में डालना और React को re-render करने देना था। यह तब ठीक काम करता है जब React synchronously render करता है, लेकिन React 18 ने concurrent rendering पेश की है, जहाँ browser द्वारा वास्तव में paint किए जाने से पहले एक component को कई बार render किया जा सकता है। इस mode में “mirror-into-local-state” pattern tearing पैदा कर सकता है—यानी एक ही render cycle के दौरान UI के अलग-अलग हिस्से एक ही external value के अलग-अलग snapshots पढ़ रहे होते हैं।

useSyncExternalStore को tearing को रोकने के लिए बनाया गया है। यह external source से एक snapshot (वर्तमान value) और एक subscribe function (बदलावों के बारे में सूचित कैसे किया जाए) मांगता है। React component के mount होने पर subscribe को call करता है और unmount होने पर स्वचालित रूप से unsubscribe कर देता है। जब भी getSnapshot द्वारा लौटाया गया snapshot बदलता है, React एक ऐसा render schedule करता है जो पूरे component tree के लिए वही snapshot पढ़ता है, जिससे consistency सुनिश्चित होती है।

दो आवश्यक functions

Function यह क्या करता है
subscribe एक callback प्राप्त करता है जिसे external value बदलने पर call किया जाना चाहिए। यह एक unsubscribe function लौटाता है जिसे React cleanup के समय call करेगा।
getSnapshot external source से वर्तमान value लौटाता है। इसे referentially stable होना चाहिए—यानी हर render पर एक ही function object—ताकि React snapshots की सही ढंग से तुलना कर सके।

Browser के online status के लिए एक minimal implementation इस प्रकार दिखता है:

function useOnlineStatus() {
  return useSyncExternalStore(
    (onChange) => {
      window.addEventListener('online', onChange);
      window.addEventListener('offline', onChange);
      return () => {
        window.removeEventListener('online', onChange);
        window.removeEventListener('offline', onChange);
      };
    },
    () => navigator.onLine
  );
}

जब भी कोई component useOnlineStatus() को call करेगा, React उसे तभी re-render करेगा जब navigator.onLine वास्तव में बदलेगा, और हर render में एक ही value दिखाई देगी।

नियम जो hook को खराब होने से बचाते हैं

  1. component को आवश्यक डेटा का सबसे छोटा हिस्सा ही लौटाएं।
    यदि external store में एक बड़ा object है लेकिन component को केवल एक field की आवश्यकता है, तो केवल वही field लौटाएं। बड़ा डेटा लौटाने से अनावश्यक renders होते हैं।

  2. getSnapshot को memoise करें।
    यदि आप हर render पर function को फिर से बनाते हैं, तो React इसे एक नया source मानता है और यह अनंत loop (infinite loop) में फंस सकता है। इसे useCallback में wrap करें या component के बाहर define करें।

  3. हर call पर नए objects लौटाने से बचें।
    एक नया object लौटाने से (जैसे, { count: store.getCount() }) हर render पर एक नया reference बनता है, जिससे React को लगता है कि snapshot बदल गया है और यह एक infinite render loop शुरू कर देता है। Primitives, strings, numbers, या memoised objects का उपयोग करें।

  4. इसका उपयोग internal component state के लिए न करें।
    जो डेटा केवल component के अंदर रहता है, उसके लिए useState अभी भी सही tool है। useSyncExternalStore अतिरिक्त overhead जोड़ता है जिसकी local state के लिए आवश्यकता नहीं है।

इसका उपयोग कब करें

  • Browser APIs – window size, media queries, navigator.onLine, battery status.
  • Framework-agnostic stores – Zustand, Redux, MobX, या कोई भी custom store जो subscription API प्रदान करता है।
  • Mutable sources जो React के बाहर update होते हैं – WebSocket messages, IndexedDB change events, Service Worker notifications.

यदि आपका data source पहले से ही React के अंदर है (जैसे, किसी parent component की state), तो useState या context का ही उपयोग करें।

community क्या कह रही है

शुरुआती उपयोगकर्ताओं (early adopters) का कहना है कि useSyncExternalStore उस flicker को खत्म कर देता है जो उन्होंने concurrent-rendering setup में windows को resize करते समय देखा था। कुछ libraries ने पहले ही अपने internal hooks को इस API पर switch कर लिया है, जो React के विभिन्न versions में अधिक predictable behavior का वादा करती हैं। इसका trade-off थोड़ा अधिक मानसिक बोझ (mental load) है: developers को दोनों functions की stability के बारे में सोचना होगा और हर call पर नए objects लौटाने से बचना होगा।

आगे क्या देखें

React के आगामी रिलीज़ useSyncExternalStore के नियमों को और अधिक सख्त बना सकते हैं, और संभवतः सामान्य ब्राउज़र APIs के लिए इन-बिल्ट हेल्पर्स भी जोड़ सकते हैं। पुराने useEffect-mirroring पैटर्न के किसी भी डिप्रिकेशन (deprecation) के लिए आधिकारिक React ब्लॉग पर नज़र रखें। इस बीच, यह हुक स्थिर है और सार्वजनिक API का हिस्सा है, इसलिए आप मौजूदा एक्सटर्नल-स्टेट (external-state) कोड को सुरक्षित रूप से रिफैक्टर कर सकते हैं।

त्वरित परीक्षण

एक लाइव डेमो जो विंडो-साइज सिंकिंग, ऑनलाइन/ऑफलाइन डिटेक्शन और एक सरल Zustand स्टोर को प्रदर्शित करता है, https://usesyncexternalstore.vercel.app/ पर उपलब्ध है। कमेंट्स के साथ पूरा सोर्स कोड https://github.com/dev48v/usesyncexternalstore पर उपलब्ध है।

मुख्य बात (Takeaway): useSyncExternalStore React को किसी भी ऐसे म्यूटेबल डेटा (mutable data) के लिए एक विश्वसनीय ब्रिज प्रदान करता है जो इसके रेंडर साइकिल के बाहर रहता है, जिससे 'tearing' और अनावश्यक रेंडरिंग को रोका जा सकता है। इसे बाहरी स्रोतों पर लागू करें, स्नैपशॉट को न्यूनतम और स्थिर रखें, और कंसिस्टेंसी बनाए रखने का मुख्य काम React पर छोड़ दें।