useSyncExternalStore-ന് കോൺകറന്റ് റെൻഡറിംഗ് (concurrent rendering) മൂലമുണ്ടാകാൻ സാധ്യതയുള്ള വിഷ്വൽ ഗ്ലിച്ചുകൾ (visual glitches) ഒഴിവാക്കിക്കൊണ്ട്, React-ന് പുറത്തുള്ള ഡാറ്റ ഒരു കമ്പോണന്റിന് വായിക്കാൻ സാധിക്കുന്നു. useEffect ഉപയോഗിച്ച് എക്സ്റ്റേണൽ വാല്യൂസ് ലോക്കൽ സ്റ്റേറ്റിലേക്ക് കൊണ്ടുവരുന്ന പഴയ രീതിക്ക് പകരമായാണ് ഇത് ഉപയോഗിക്കുന്നത്. ഈ പഴയ രീതി "tearing" എന്ന പ്രശ്നത്തിന് കാരണമായേക്കാം - അതായത് ഒരേ റെൻഡറിംഗിനിടെ തന്നെ UI-യുടെ വിവിധ ഭാഗങ്ങൾ വ്യത്യസ്ത വാല്യൂകൾ കാണിക്കുന്ന അവസ്ഥ.
എന്തുകൊണ്ടാണ് പഴയ രീതി കോൺകറൻസിയിൽ പരാജയപ്പെടുന്നത്
React 18-ന് മുമ്പ് സാധാരണയായി ഉപയോഗിച്ചിരുന്ന രീതി ഇതാണ്:
const [value, setValue] = useState(initial);
useEffect(() => {
const unsubscribe = externalSource.subscribe(v => setValue(v));
return unsubscribe;
}, []);
കമ്പോണന്റ് റെൻഡർ ചെയ്തതിന് ശേഷമാണ് effect പ്രവർത്തിക്കുന്നത്. React-ന്റെ കോൺകറന്റ് റെൻഡറർ ഒരു റെൻഡറിംഗ് നിർത്തിവെക്കാനോ (pause), റദ്ദാക്കാനോ (abort), അല്ലെങ്കിൽ വീണ്ടും ചെയ്യാനോ (replay) തീരുമാനിക്കുമ്പോൾ, സ്റ്റേറ്റ് വായിക്കുന്ന UI-യിൽ നിന്നും വ്യത്യസ്തമായ ഒരു സമയത്തായിരിക്കാം effect പ്രവർത്തിക്കുന്നത്. ഇതിന്റെ ഫലമായി ഒരു പൊരുത്തക്കേട് (mismatch) ഉണ്ടാകുന്നു: ഒരു കമ്പോണന്റ് പഴയ വാല്യൂ വായിച്ചേക്കാം, മറ്റൊന്ന് പുതിയ വാല്യൂ വായിച്ചേക്കാം, തൽഫലമായി സ്ക്രീനിൽ അല്പനേരത്തേക്ക് തെറ്റായ ഡാറ്റ കാണപ്പെടുന്നു. പൂർണ്ണമായും സിൻക്രണസ് (synchronous) ആയ ഒരു സാഹചര്യത്തിൽ ഈ "tearing" പ്രശ്നമുണ്ടാക്കില്ലെങ്കിലും, React ഒരേസമയം UI-യുടെ ഒന്നിലധികം പതിപ്പുകൾ റെൻഡർ ചെയ്യുമ്പോൾ ഇത് ഒരു വിഷ്വൽ ബഗ് ആയി മാറുന്നു.
എങ്ങനെ useSyncExternalStore ഈ പ്രശ്നം പരിഹരിക്കുന്നു
ഈ ഹുക്കിന് രണ്ട് കോൾബാക്കുകൾ (callbacks) ആവശ്യമാണ്:
- subscribe – ഒരു ലിസണറെ രജിസ്റ്റർ ചെയ്യുന്നു, കൂടാതെ അത് നീക്കം ചെയ്യാനുള്ള ഒരു ഫങ്ക്ഷൻ തിരികെ നൽകുന്നു.
- getSnapshot – എക്സ്റ്റേണൽ സോഴ്സിന്റെ നിലവിലെ വാല്യൂ തിരികെ നൽകുന്നു.
കമ്പോണന്റ് മൗണ്ട് (mount) ചെയ്യുമ്പോൾ React subscribe വിളിക്കുന്നു, മൗണ്ട് അൺമൗണ്ട് (unmount) ചെയ്യുമ്പോൾ അത് ഒഴിവാക്കുന്നു. ഓരോ റെൻഡറിംഗിനിടെയും ഇത് getSnapshot വിളിക്കുകയും, ഒരേ എക്സ്റ്റേണൽ സ്റ്റോർ ഉപയോഗിക്കുന്ന എല്ലാ കമ്പോണന്റുകളും കൃത്യമായ ഒരേ സ്നാപ്ഷോട്ട് (snapshot) തന്നെ വായിക്കുന്നുണ്ടെന്ന് ഉറപ്പാക്കുകയും ചെയ്യുന്നു. ഡാറ്റ വായിക്കുന്നത് റെൻഡർ ഫേസിനുള്ളിൽ (render phase) ആയതുകൊണ്ട്, റെൻഡറർക്ക് മുഴുവൻ ട്രീയിലുടനീളമുള്ള അപ്ഡേറ്റുകൾ ഏകോപിപ്പിക്കാൻ സാധിക്കുന്നു, ഇത് tearing ഒഴിവാക്കുന്നു.
Zustand, Redux തുടങ്ങിയ ലൈബ്രറികൾ ഇതിനകം തന്നെ ഈ ഹുക്ക് ഉപയോഗിച്ച് അവരുടെ സ്റ്റോറുകളെ പൊതിഞ്ഞിട്ടുണ്ട് (wrap), അതുകൊണ്ടാണ് അവ React 18-ന്റെ കോൺകറന്റ് ഫീച്ചറുകളുമായി നേരിട്ട് പ്രവർത്തിക്കുന്നത്.
ഹുക്ക് തെറ്റായി പ്രവർത്തിക്കാതിരിക്കാനുള്ള നിയമങ്ങൾ
- കമ്പോണന്റിന് ആവശ്യമുള്ളത് മാത്രം തിരികെ നൽകുക – വലിയൊരു ഒബ്ജക്റ്റ് നൽകുന്നത് ഓരോ അപ്ഡേറ്റിലും ഡീപ്പ് സ്ട്രക്ചറുകൾ (deep structures) താരതമ്യം ചെയ്യാൻ React-നെ നിർബന്ധിക്കുന്നു. ചെറിയൊരു സ്നാപ്ഷോട്ട് ഉപയോഗിക്കുന്നത് മാറ്റങ്ങൾ കണ്ടെത്തുന്നത് എളുപ്പമാക്കുന്നു.
- റഫറൻഷ്യൽ ഐഡന്റിറ്റി (referential identity) നിലനിർത്തുക – എക്സ്റ്റേണൽ വാല്യൂയിൽ മാറ്റം വന്നിട്ടില്ലെങ്കിൽ, getSnapshot ഒരേ ഒബ്ജക്റ്റ് റഫറൻസ് തന്നെ തിരികെ നൽകണം. ഓരോ തവണയും പുതിയൊരു ഒബ്ജക്റ്റ് നൽകുന്നത് ഇൻഫിനിറ്റ് റെൻഡർ ലൂപ്പിന് (infinite render loop) കാരണമാകും, കാരണം ഓരോ റെൻഡറിംഗിലും മാറ്റം ഉണ്ടെന്ന് React കരുതും.
- എക്സ്റ്റേണൽ സോഴ്സുകൾക്കായി മാത്രം പരിമിതപ്പെടുത്തുക – ലോക്കൽ കമ്പോണന്റ് സ്റ്റേറ്റിനായി
useStateഅല്ലെങ്കിൽuseReducerഉപയോഗിക്കുക. വെറും ഇന്റേണൽ വാല്യൂകൾക്കായി ഈ ഹുക്ക് ഉപയോഗിക്കുന്നത് അനാവശ്യമായ സങ്കീർണ്ണത ഉണ്ടാക്കും.
എപ്പോഴാണ് useSyncExternalStore ഉപയോഗിക്കേണ്ടത്
- ബ്രൗസർ API-കളെ (window size, network-online status, media queries) റാപ്പ് ചെയ്യാൻ.
- React-നെ കുറിച്ച് അറിയാത്ത ഒരു പ്ലെയിൻ ജാവാസ്ക്രിപ്റ്റ് സ്റ്റോറുമായി ബന്ധിപ്പിക്കാൻ.
- പ്രോപ്പ്-ഡ്രിളിംഗിന് (prop-drilling) പകരമായി, പല കമ്പോണന്റുകളും സബ്സ്ക്രൈബ് ചെയ്യുന്ന ഒരു ഗ്ലോബൽ റീഡ്-ഒൺലി സോഴ്സ് ഉപയോഗിക്കാൻ.
വിപരീത വാദം
ലളിതമായതും ഇടയ്ക്കിടെ മാറാത്തതുമായ ഡാറ്റയ്ക്ക്, പ്രത്യേകിച്ച് കോൺകറന്റ് ഫീച്ചറുകൾ ഉപയോഗിക്കാത്ത ആപ്ലിക്കേഷനുകളിൽ, useState + useEffect രീതി ഇപ്പോഴും ഫലപ്രദമാണ്. സബ്സ്ക്രിപ്ഷൻ ലോജിക് എഫക്റ്റിൽ (effect) വ്യക്തമായി കാണാൻ കഴിയുന്നതിനാൽ ചില ഡെവലപ്പർമാർക്ക് ഇത് എളുപ്പമായി തോന്നാം. എന്നാൽ, കോൺകറന്റ് റെൻഡറിംഗ് ഉപയോഗിക്കുമ്പോൾ "tearing" ഉണ്ടാകാനുള്ള സാധ്യത ഇതിലുണ്ട്. React 18-ന്റെ പുതിയ ഫീച്ചറുകൾ ഉപയോഗിക്കുന്ന പ്രോജക്റ്റുകളിൽ, ചെറിയൊരു അധിക കോഡ് (boilerplate) എഴുതുന്നതിനേക്കാൾ useSyncExternalStore നൽകുന്ന സുരക്ഷയാണ് പ്രധാനം.
ശ്രദ്ധിക്കേണ്ട കാര്യങ്ങൾ
- നിങ്ങളുടെ സ്റ്റോറിലെ getSnapshot ഒരു സ്റ്റേബിൾ റഫറൻസ് (stable reference) നൽകുന്നുണ്ടെന്ന് ഉറപ്പുവരുത്തുക; ഡെവലപ്മെന്റ് സമയത്ത് റഫറൻസ്
console.logചെയ്യുന്നത് അബദ്ധവശാൽ ഒബ്ജക്റ്റുകൾ വീണ്ടും നിർമ്മിക്കപ്പെടുന്നത് കണ്ടെത്താൻ സഹായിക്കും. - സബ്സ്ക്രിപ്ഷൻ ഫങ്ക്ഷൻ ലഘുവായി സൂക്ഷിക്കുക – കഠിനമായ ജോലികൾ എക്സ്റ്റേണൽ സോഴ്സിനുള്ളിൽ ആയിരിക്കണം, React-ലേക്ക് നൽകുന്ന കോൾബാക്കിൽ ആകരുത്.
- കോൺകറന്റ് മോഡിൽ (ഉദാഹരണത്തിന്
ReactDOM.createRoot-നോടൊപ്പംactഉപയോഗിച്ച്) കമ്പോണന്റുകൾ ടെസ്റ്റ് ചെയ്ത് "tearing" ഉണ്ടാകുന്നില്ലെന്ന് ഉറപ്പാക്കുക.
React-ന് പുറത്തുള്ള ഒരു സിംപിൾ കൗണ്ടർ സിങ്ക് ചെയ്തും വിഷ്വൽ ഗ്ലിച്ചുകൾ ഇല്ലാതെ ഒന്നിലധികം കമ്പോണന്റുകൾ അപ്ഡേറ്റ് ചെയ്യുന്നതും കാണിക്കുന്ന ഒരു ലൈവ് ഡെമോ ലഭ്യമാണ്. ഡെമോ ഈ ലിങ്കിൽ കാണാം: https://usesyncexternalstore.vercel.app/. ഇതിന്റെ സോഴ്സ് കോഡ് GitHub-ൽ ലഭ്യമാണ്, കൂടാതെ ഇതിന്റെ കൂടുതൽ വിശദാംശങ്ങൾ ഒരു വലിയ ലേഖനത്തിൽ വിവരിച്ചിട്ടുണ്ട്.
ചുരുക്കത്തിൽ: React-ന് UI-യുടെ ഭാഗങ്ങൾ ക്രമരഹിതമായി റെൻഡർ ചെയ്യാൻ കഴിയുന്ന സാഹചര്യത്തിൽ, എക്സ്റ്റേണൽ ഡാറ്റയും കമ്പോണന്റ് റെൻഡറിംഗും തമ്മിലുള്ള വിശ്വസനീയമായ പാലമാണ് useSyncExternalStore. ഇത് വിഷ്വൽ കൺസിസ്റ്റൻസി (visual consistency) നിലനിർത്തുന്നു, ഗ്ലോബൽ സ്റ്റോറുകൾക്കായി ഉപയോഗിക്കാം, കൂടാതെ പ്രശസ്തമായ സ്റ്റേറ്റ് മാനേജ്മെന്റ് ലൈബ്രറികളിൽ ഇത് ഇതിനകം തന്നെ ഉൾപ്പെടുത്തിയിട്ടുണ്ട്. React-നല്ലാത്ത സ്റ്റേറ്റ് വായിക്കേണ്ടി വരുമ്പോഴെല്ലാം ഇത് ഉപയോഗിക്കുക; അതിന്റെ പൂർണ്ണമായ ഗുണങ്ങൾ ലഭിക്കാൻ സ്നാപ്ഷോട്ടുകൾ ചെറുതും സ്റ്റേബിളും ആയി സൂക്ഷിക്കുക.
