React 18 نے useSyncExternalStore شامل کیا ہے، یہ ایک ایسا hook ہے جو components کو React tree سے باہر موجود کسی بھی ذریعے (source) سے ڈیٹا پڑھنے کی اجازت دیتا ہے—جیسے browser APIs، WebSockets، یا third-party stores—بغیر ان بصری خرابیوں (visual glitches) کے جو useState اور useEffect کے ذریعے ڈیٹا کو mirror کرنے کے پرانے طریقے میں پیدا ہوتی تھیں۔ اگر آپ نے کبھی UI کو جھپکتے (flicker) ہوئے دیکھا ہے کیونکہ اس کا ایک حصہ پرانی window size دکھا رہا تھا جبکہ دوسرا حصہ نئی size دکھا رہا تھا، تو یہ hook وہی حل ہے جس کا آپ کو انتظار تھا۔

بیرونی اسٹیٹ (external state) کو نئے hook کی ضرورت کیوں ہے

React components ہمیشہ سے ایسی چیزوں کو subscribe کرنے کے قابل رہے ہیں جو ان کے اپنے دائرہ کار (scope) سے باہر موجود ہوں: جیسے window object، Redux store، یا WebSocket connection۔ روایتی طریقہ یہ تھا کہ useEffect کے اندر ایک subscription سیٹ اپ کی جائے، setState کے ذریعے آنے والی ویلیوز کو local state میں ڈالا جائے، اور React کو re-render کرنے دیا جائے۔ یہ تب تو ٹھیک کام کرتا ہے جب React synchronously render کرتا ہے، لیکن React 18 نے concurrent rendering متعارف کرائی ہے، جہاں ایک component کو browser کے اصل میں paint کرنے سے پہلے کئی بار render کیا جا سکتا ہے۔ اس موڈ میں "mirror-into-local-state" کا طریقہ کار tearing پیدا کر سکتا ہے—یعنی ایک ہی render cycle کے دوران UI کے مختلف حصے ایک ہی بیرونی ویلیو کے مختلف snapshots پڑھ رہے ہوں۔

useSyncExternalStore کو tearing کو روکنے کے لیے بنایا گیا ہے۔ یہ بیرونی ذریعے (external source) سے ایک snapshot (موجودہ ویلیو) اور ایک subscribe function (تبدیلیوں کے بارے میں مطلع ہونے کا طریقہ) مانگتا ہے۔ React component کے mount ہونے پر subscribe کو کال کرتا ہے اور unmount ہونے پر خود بخود unsubscribe کر دیتا ہے۔ جب بھی getSnapshot سے واپس آنے والا snapshot تبدیل ہوتا ہے، React ایک ایسا render شیڈول کرتا ہے جو پورے component tree کے لیے ایک ہی snapshot پڑھتا ہے، جس سے تسلسل (consistency) یقینی بنتا ہے۔

دو ضروری functions

Function یہ کیا کرتا ہے
subscribe ایک callback وصول کرتا ہے جسے بیرونی ویلیو تبدیل ہونے پر کال کرنا ضروری ہے۔ یہ ایک unsubscribe function واپس کرتا ہے جسے React cleanup کے وقت کال کرے گا۔
getSnapshot بیرونی ذریعے سے موجودہ ویلیو واپس کرتا ہے۔ اسے referentially stable ہونا چاہیے—یعنی ہر render پر ایک ہی function object—تاکہ React snapshots کا درست طریقے سے موازنہ کر سکے۔

Browser کے online اسٹیٹس کے لیے ایک سادہ سی مثال (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() کو کال کرے گا، React اسے صرف اسی وقت re-render کرے گا جب navigator.onLine واقعی تبدیل ہوگا، اور ہر render میں ایک ہی ویلیو نظر آئے گی۔

وہ اصول جو hook کو خراب ہونے سے بچاتے ہیں

  1. صرف وہی ڈیٹا واپس کریں جس کی component کو ضرورت ہے۔
    اگر بیرونی store میں ایک بڑا object ہے لیکن component کو صرف ایک field سے مطلب ہے، تو صرف وہی field واپس کریں۔ بڑا ڈیٹا واپس کرنے سے غیر ضروری renders ہوتے ہیں۔

  2. getSnapshot کو memoise کریں۔
    اگر آپ ہر render پر function کو دوبارہ بناتے ہیں، تو React اسے ایک نیا source سمجھے گا اور یہ ہمیشہ کے لیے loop میں پھنس سکتا ہے۔ اسے useCallback میں لپیٹ دیں یا component سے باہر تعریف (define) کریں۔

  3. ہر بار نیا object واپس کرنے سے گریز کریں۔
    ایک نیا object واپس کرنا (مثلاً { count: store.getCount() }) ہر render پر ایک نیا reference تخلیق کرتا ہے، جس سے React کو لگتا ہے کہ snapshot تبدیل ہو گیا ہے اور یہ ایک infinite render loop شروع کر دیتا ہے۔ primitives، strings، numbers، یا memoised objects استعمال کریں۔

  4. اسے internal component state کے لیے استعمال نہ کریں۔
    وہ ڈیٹا جو صرف component کے اندر رہتا ہے، اس کے لیے useState اب بھی بہترین ٹول ہے۔ 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 سے باہر اپ ڈیٹ ہوتے ہیں – WebSocket messages، IndexedDB change events، Service Worker notifications۔

اگر آپ کا ڈیٹا source پہلے سے ہی React کے اندر ہے (مثلاً کسی parent component کی state)، تو useState یا context ہی استعمال کریں۔

کمیونٹی کا کیا کہنا ہے

ابتدائی صارفین (early adopters) کا کہنا ہے کہ useSyncExternalStore اس جھپک (flicker) کو ختم کر دیتا ہے جو انہوں نے concurrent-rendering سیٹ اپ میں windows کو resize کرتے وقت دیکھا تھا۔ کچھ libraries پہلے ہی اپنے internal hooks کو اس API پر منتقل کر چکی ہیں، جس سے React کے مختلف ورژنز میں زیادہ قابلِ پیش گوئی (predictable) رویے کا وعدہ کیا گیا ہے۔ اس کا ایک نقصان تھوڑا زیادہ ذہنی بوجھ ہے: ڈویلپرز کو ان دو functions کی stability کے بارے میں سوچنا ہوگا اور ہر بار نیا object واپس کرنے سے بچنا ہوگا۔

آگے کیا دیکھنا ہے

React کی آنے والی ریلیزز useSyncExternalStore کے گرد کنٹریکٹ کو مزید سخت کر سکتی ہیں، جس میں ممکنہ طور پر عام براؤزر APIs کے لیے بلٹ ان ہیلپرز شامل کیے جا سکتے ہیں۔ پرانے useEffect-mirroring پیٹرن کی کسی بھی قسم کی ڈیپریکیشن (deprecation) کے لیے آفیشل React بلاگ پر نظر رکھیں۔ اس دوران، یہ ہوک مستحکم ہے اور پبلک API کا حصہ ہے، اس لیے آپ موجودہ ایکسٹرنل اسٹیٹ کوڈ کو بلا خوف و خطر ریفیکٹر (refactor) کر سکتے ہیں۔

فوری ٹیسٹ ڈرائیو

ایک لائیو ڈیمو جو ونڈو سائز سنکرونائزیشن، آن لائن/آف لائن ڈیٹیکشن، اور ایک سادہ Zustand اسٹور کو ظاہر کرتا ہے، اس لنک پر دستیاب ہے: https://usesyncexternalstore.vercel.app/۔ کمنٹس کے ساتھ مکمل سورس کوڈ یہاں دستیاب ہے: https://github.com/dev48v/usesyncexternalstore۔

خلاصہ: useSyncExternalStore React کو کسی بھی ایسے متغیر ڈیٹا (mutable data) کے لیے ایک قابل اعتماد پل فراہم کرتا ہے جو اس کے رینڈر سائیکل سے باہر ہوتا ہے، جس سے ٹیئرنگ (tearing) اور غیر ضروری رینڈرز سے بچا جا سکتا ہے۔ اسے بیرونی ذرائع پر لاگو کریں، اسنیپ شاٹ کو کم سے کم اور مستحکم رکھیں، اور کنسسٹنسی برقرار رکھنے کا بھاری کام React پر چھوڑ دیں۔