React നിങ്ങളുടെ കംപോണന്റുകൾ പ്രവചിക്കാവുന്നതാകാൻ (predictable) ആഗ്രഹിക്കുന്നു. ഒരേ സ്റ്റേറ്റും പ്രോപ്‌സും (state and props) നൽകിയാൽ, അത് എപ്പോഴും ഒരേ UI തന്നെ നൽകണം. എന്നാൽ മിക്ക യഥാർത്ഥ ആപ്ലിക്കേഷനുകൾക്കും ആ പരിധിക്കുള്ളിൽ മാത്രം ഒതുങ്ങിനിൽക്കാൻ കഴിയില്ല. അവയ്ക്ക് പുറംലോകവുമായി ബന്ധപ്പെടേണ്ടി വരും. ഒരു ഡാഷ്‌ബോർഡിന് സെർവറിൽ നിന്ന് പുതിയ വിവരങ്ങൾ ആവശ്യമാണ്. ഒരു ചാറ്റ് വിഡ്ജറ്റിന് സന്ദേശങ്ങൾക്കായി കാത്തിരിക്കേണ്ടതുണ്ട്. ഒരു ടൈമർ പ്രവർത്തിച്ചുകൊണ്ടിരിക്കണം. ഇത്തരം പ്രവർത്തനങ്ങൾ സൈഡ് ഇഫക്റ്റുകളാണ് (side effects), അവ React-ന്റെ റെൻഡർ സൈക്കിളിന് പുറത്താണ് പ്രവർത്തിക്കുന്നത്. നിങ്ങളുടെ കംപോണന്റ് കൃത്യമായി നിലനിൽക്കുന്നതിനായി, ഈ സങ്കീർണ്ണവും പ്രവചനാതീതവുമായ ജോലികൾ ചെയ്യാൻ ഉപയോഗിക്കുന്നതാണ് useEffect ഹുക്ക്.

Side Effects: എന്തൊക്കെയാണ് useEffect-ൽ ഉൾപ്പെടേണ്ടത്?

JSX റിട്ടേൺ ചെയ്യുന്നതിനപ്പുറം ലോകവുമായി ബന്ധപ്പെടുന്ന ഏത് കാര്യവും ഒരു സൈഡ് ഇഫക്റ്റ് ആണ്. React-ന്റെ റെൻഡർ ഫേസ് (render phase) ശുദ്ധമായിരിക്കണം (pure). നിങ്ങൾ ഡാറ്റ ഫെച്ച് ചെയ്യാനോ (fetching data), ഗ്ലോബൽ വേരിയബിളുകളിൽ എഴുതാനോ, അല്ലെങ്കിൽ DOM-ൽ ലിസണറുകൾ (listeners) ഘടിപ്പിക്കാനോ തുടങ്ങുമ്പോൾ, നിങ്ങൾ ആ ശുദ്ധമായ പരിധിയിൽ നിന്ന് പുറത്തുവരുന്നു.

സാധാരണ ഉദാഹരണങ്ങൾ ഇവയാണ്:

  • ഒരു API-ൽ നിന്ന് ഡാറ്റ ഫെച്ച് ചെയ്യുക
  • ടൈമറുകളോ ഇന്റർവലുകളോ (timers or intervals) സെറ്റ് ചെയ്യുക
  • window അല്ലെങ്കിൽ document-ൽ ഇവന്റ് ലിസണറുകൾ (event listeners) ചേർക്കുക
  • ബ്രൗസർ ടാബ് ടൈറ്റിൽ അപ്ഡേറ്റ് ചെയ്യുക
  • WebSockets-ലേക്ക് കണക്ട് ചെയ്യുക

ഈ ജോലികൾക്കെല്ലാം ഒരു പൊതുവായ സവിശേഷതയുണ്ട്: അവ നിങ്ങളുടെ കംപോണന്റിന്റെ return സ്റ്റേറ്റ്‌മെന്റിനുള്ളിലോ പ്രധാന റെൻഡർ ലോജിക്കിനുള്ളിലോ ഉൾപ്പെടേണ്ടവയല്ല. setInterval-നെപ്പോലെയുള്ള ഒരു ബ്രൗസർ API നേരിട്ട് റെൻഡർ ബോഡിയിൽ വിളിക്കാൻ ശ്രമിച്ചാൽ, അത് ഓരോ റെൻഡറിനും വീണ്ടും വീണ്ടും പ്രവർത്തിക്കുകയും ഡ്യൂപ്ലിക്കേറ്റ് ടൈമറുകൾ സൃഷ്ടിക്കുകയും ആശയക്കുഴപ്പമുണ്ടാക്കുകയും ചെയ്യും. ഈ ജോലികളെ വേർതിരിക്കാനും അവ ശരിയായ സമയത്ത് പ്രവർത്തിപ്പിക്കാനും വേണ്ടിയാണ് useEffect നിലകൊള്ളുന്നത്.

Dependency Array എങ്ങനെയാണ് സമയക്രമം നിയന്ത്രിക്കുന്നത്?

useEffect-ന്റെ രണ്ടാമത്തെ ആർഗ്യുമെന്റ് ആണ് dependency array. ക്ലാസ് കംപോണന്റുകളിൽ നിന്ന് മാറുന്ന ഡെവലപ്പർമാർക്കിടയിൽ ഏറ്റവും കൂടുതൽ ആശയക്കുഴപ്പമുണ്ടാക്കുന്ന കാര്യമാണിത്. നിലവിലെ റെൻഡറിന് ശേഷം നിങ്ങളുടെ ഇഫക്റ്റ് ഒഴിവാക്കണോ അതോ പ്രവർത്തിപ്പിക്കണോ എന്ന് തീരുമാനിക്കാൻ React നിരീക്ഷിക്കുന്ന വേരിയബിളുകളുടെ ഒരു കൂട്ടമായി ഇതിനെ കരുതാം.

താഴെ പറയുന്ന മൂന്ന് രീതികൾ നിങ്ങൾ ആവർത്തിച്ച് ഉപയോഗിക്കും.

Dependency array ഒട്ടുമില്ല. നിങ്ങൾ ആറം പൂർണ്ണമായും ഒഴിവാക്കിയാൽ, ആദ്യത്തെ റെൻഡർ ഉൾപ്പെടെ ഓരോ തവണ റെൻഡർ ചെയ്യുമ്പോഴും ഇഫക്റ്റ് പ്രവർത്തിപ്പിക്കണമെന്ന് React കരുതുന്നു. ഇത് മിക്കവാറും ആവശ്യമുള്ള ഒന്നല്ല. നിങ്ങളുടെ ഇഫക്റ്റ് ഒരു നെറ്റ്‌വർക്ക് റിക്വസ്റ്റോ അല്ലെങ്കിൽ വലിയൊരു DOM ഓപ്പറേഷനോ ആണെങ്കിൽ, ഓരോ കീസ്ട്രോക്കിനും സ്റ്റേറ്റ് മാറ്റങ്ങൾക്കും അനുസരിച്ച് അത് പ്രവർത്തിക്കുന്നത് പെർഫോമൻസ് കുറയ്ക്കും. ഏതെങ്കിലും പ്രോപ്പിലോ സ്റ്റേറ്റിലോ മാറ്റം വന്നാൽ അത് പ്രവർത്തിപ്പിക്കണമെന്ന് നിങ്ങൾക്ക് കൃത്യമായി പറയാൻ കഴിയില്ലെങ്കിൽ മാത്രം ഈ രീതി ഉപയോഗിക്കുക.

ഒരു ശൂന്യമായ അറേ []. കംപോണന്റ് മൗണ്ട് (mount) ചെയ്ത് DOM തയ്യാറായ ഉടൻ തന്നെ ഇഫക്റ്റ് ഒരു തവണ മാത്രം പ്രവർത്തിപ്പിക്കാൻ ഇത് React-നോട് പറയുന്നു. പ്രാരംഭ ഡാറ്റ ഫെച്ച് ചെയ്യുന്നതിന് (initial data fetches) അനുയോജ്യമായ സ്ഥലമാണിത്. ഉദാഹരണത്തിന്, നിങ്ങളുടെ കംപോണന്റ് ഒരു യൂസർ പ്രൊഫൈൽ ഡാറ്റ ലോഡ് ചെയ്യുന്നുണ്ടെങ്കിൽ, പ്രൊഫൈൽ പേജ് വരുമ്പോൾ മാത്രം ആ റിക്വസ്റ്റ് ഒരു തവണ പ്രവർത്തിക്കണം, അല്ലാതെ പേജിലെ ഫോമുകളിൽ ഉപയോക്താവ് എന്തെങ്കിലും മാറ്റം വരുത്തുമ്പോഴെല്ലാം അത് പ്രവർത്തിക്കരുത്.

പ്രത്യേക വേരിയബിളുകൾ അടങ്ങിയ അറേ [count]. ഇതൊരു കൃത്യതയുള്ള ടൂൾ ആണ്. ഈ ഡിപൻഡൻസികളുടെ നിലവിലെ മൂല്യങ്ങളെ (values) കഴിഞ്ഞ റെൻഡറിലെ മൂല്യങ്ങളുമായി React താരതമ്യം ചെയ്യുന്നു. അവയിൽ ഏതെങ്കിലും മാറിയാൽ ഇഫക്റ്റ് പ്രവർത്തിക്കും. ലിസ്റ്റിലുള്ള ഒന്നിലും മാറ്റം വന്നില്ലെങ്കിൽ React ആ ഇഫക്റ്റ് പൂർണ്ണമായും ഒഴിവാക്കും.

നിങ്ങൾ ബ്രൗസർ ടാബ് ടൈറ്റിലിനെ ഒരു സ്റ്റേറ്റ് വേരിയബിളുമായി സിങ്ക് (sync) ചെയ്യുകയാണെങ്കിൽ, ആ വേരിയബിൾ dependency array-യിൽ നൽകണം. അപ്പോൾ ആ മൂല്യം മാറുമ്പോൾ മാത്രമേ React ടൈറ്റിൽ അപ്ഡേറ്റ് ചെയ്യുകയുള്ളൂ. അത് നൽകിയില്ലെങ്കിൽ ടൈറ്റിൽ പഴയത് തന്നെയായിരിക്കും. അനാവശ്യമായ സ്റ്റേറ്റ് വേരിയബിളുകൾ നൽകിയാൽ, മാറ്റം ആവശ്യമില്ലാത്ത കാര്യങ്ങൾക്കായി സൈക്കിളുകൾ പാഴാക്കേണ്ടി വരും.

Cleanup എന്നത് ഒഴിവാക്കാനാവാത്തതാണ്

ചില ഇഫക്റ്റുകൾ അവശേഷിച്ച പാടുകൾ ബാക്കിവെക്കും. ഒരു ടൈമർ എണ്ണിക്കൊണ്ടിരിക്കും. ഒരു ഇവന്റ് ലിസണർ പ്രവർത്തിച്ചുകൊണ്ടിരിക്കും. ഒരു WebSocket തുറന്നുതന്നെ ഇരിക്കും. നിങ്ങളുടെ കംപോണന്റ് അൺമൗണ്ട് (unmount) ചെയ്യുമ്പോഴോ, അല്ലെങ്കിൽ ഡിപൻഡൻസികൾ മാറിയതുകൊണ്ട് ഒരു ഇഫക്റ്റ് വീണ്ടും പ്രവർത്തിക്കുമ്പോഴോ, പഴയ ഇഫക്റ്റിന്റെ അവശിഷ്ടങ്ങൾ വൃത്തിയാക്കാൻ React സ്വയം തയ്യാറാവില്ല. അത് നിങ്ങളുടെ ജോലിയാണ്.

useEffect-നുള്ളിൽ നിന്ന് ഒരു ഫംഗ്ഷൻ റിട്ടേൺ ചെയ്യുന്നതിലൂടെ നിങ്ങൾക്ക് ഒരു ക്ലീനപ്പ് ഫംഗ്ഷൻ (cleanup function) നിർമ്മിക്കാം. അടുത്ത ഇഫക്റ്റ് നടപ്പിലാക്കുന്നതിന് മുമ്പും, കംപോണന്റ് സ്ക്രീനിൽ നിന്ന് മാറുന്ന സമയത്തും React ഈ ക്ലീനപ്പ് ഫംഗ്ഷൻ വിളിക്കുന്നു.

താഴെ പറയുന്ന കാര്യങ്ങൾക്കായി ക്ലീനപ്പ് ഉപയോഗിക്കണം:

  • clearInterval അല്ലെങ്കിൽ clearTimeout ഉപയോഗിച്ച് ഇന്റർവലുകളോ ടൈമൗട്ടുകളോ ക്ലിയർ ചെയ്യുക
  • window, document അല്ലെങ്കിൽ മറ്റ് എക്സ്റ്റേണൽ നോഡുകളിൽ ചേർത്ത ഇവന്റ് ലിസണറുകൾ നീക്കം ചെയ്യുക
  • ഡാറ്റ സ്ട്രീമുകളിൽ നിന്നോ സർവീസുകളിൽ നിന്നോ അൺസബ്‌സ്‌ക്രൈബ് (unsubscribe) ചെയ്യുക

ഇത് ശ്രദ്ധിച്ചില്ലെങ്കിൽ മെമ്മറി ലീക്കുകൾ (memory leaks) ഉണ്ടാകാം. ഒരു കംപോണന്റ് മൗണ്ട് ആകുന്നു, ഒരു സ്ക്രോൾ ലിസണർ ഘടിപ്പിക്കുന്നു, പിന്നീട് അൺമൗണ്ട് ആകുന്നു, പക്ഷേ ആ ലിസണർ അവിടെത്തന്നെ അവശേഷിക്കുന്നു. ബ്രൗസർ ആ കോൾബാക്കും (callback) അത് റഫർ ചെയ്യുന്ന DOM നോഡുകളും പിടിച്ചുവെക്കുന്നു. കാലക്രമേണ, പ്രത്യേകിച്ച് കൂടുതൽ നാവിഗേഷൻ ഉള്ള സിംഗിൾ പേജ് ആപ്ലിക്കേഷനുകളിൽ, ഇത്തരം പ്രശ്നങ്ങൾ കുന്നുകൂടുകയും ടാബ് സ്ലോ ആകുകയും ചെയ്യും. ഇതിനുള്ള പരിഹാരം വളരെ ലളിതമാണ്: നിങ്ങൾ ചേർത്ത കാര്യങ്ങൾ നീക്കം ചെയ്യുന്ന ഒരു ഫംഗ്ഷൻ റിട്ടേൺ ചെയ്യുക.

പ്രൊഡക്ഷനിലേക്ക് എത്തുന്ന സാധാരണ തെറ്റുകൾ

പരിചയസമ്പന്നരായ ഡെവലപ്പർമാർ പോലും ലളിതമായ മറ്റ് മാർഗങ്ങൾ ലഭ്യമായിരിക്കുമ്പോഴും useEffect ഉപയോഗിക്കാറുണ്ട്. കോഡ് റിവ്യൂ സമയത്ത് ശ്രദ്ധിക്കേണ്ട മൂന്ന് രീതികൾ താഴെ പറയുന്നവയാണ്.

അനന്തമായ ലൂപ്പുകൾ (Infinite loops). ആ സ്റ്റേറ്റ് വേരിയബിൾ നിങ്ങളുടെ dependency array-ൽ ഉണ്ടെങ്കിൽ, ആ ചക്രം തടയാൻ ഒരു കണ്ടീഷൻ ഇല്ലെങ്കിൽ, useEffect-നുള്ളിൽ ആ സ്റ്റേറ്റ് വേരിയബിൾ ഒരിക്കലും അപ്‌ഡേറ്റ് ചെയ്യരുത്. നിങ്ങൾ count വായിക്കുകയും, അത് വർദ്ധിപ്പിക്കുകയും, count-നെ ഒരു dependency ആയി നൽകുകയും ചെയ്താൽ, ആ മാറ്റം തിരിച്ചറിഞ്ഞ് React വീണ്ടും റീ-റെൻഡർ ചെയ്യും, ഇഫക്റ്റ് വീണ്ടും പ്രവർത്തിപ്പിക്കും, വീണ്ടും വർദ്ധിപ്പിക്കും, അങ്ങനെ ബ്രൗസർ ഹാങ്ങ് ആയിപ്പോകും.

അനാവശ്യമായ ഇഫക്റ്റുകൾ (Unnecessary effects). നിലവിലുള്ള props അല്ലെങ്കിൽ state ഉപയോഗിച്ച് ഒരു മൂല്യം (value) കണക്കാക്കാൻ useEffect ഉപയോഗിക്കരുത്. റെൻഡറിംഗിനിടെ നേരിട്ട് കണക്കാക്കാൻ കഴിയുമെങ്കിൽ, അത് അപ്പൊൾ തന്നെ ചെയ്യുക. ഇത്തരം മൂല്യങ്ങൾ കോംപോണന്റ് ബോഡിയിലോ അല്ലെങ്കിൽ useMemo ഉപയോഗിച്ചുള്ള memoized calculation-ലോ ആണ് ഉൾപ്പെടുത്തേണ്ടത്. അവയെ ഒരു ഇഫക്റ്റിലേക്ക് മാറ്റുന്നത് നിങ്ങളുടെ ലോജിക്കിനെ റെൻഡർ, ഇഫക്റ്റ് ഘട്ടങ്ങളിലായി അനാവശ്യമായി വിഭജിക്കുകയും കോഡ് മനസ്സിലാക്കാൻ പ്രയാസകരമാക്കുകയും ചെയ്യുന്നു.

യൂസർ ആക്ഷനുകൾക്കായി തെറ്റായ ടൂൾ ഉപയോഗിക്കുന്നത്. use