Hooks-ന്റെ നിയമങ്ങൾ

React 16.8 ലെ പുതിയതായി ചേർക്കപ്പെട്ട ഫീച്ചർ ആണ് Hooks. class എഴുതാതെ state ഉം മറ്റ് React ഫീച്ചറുകളും ഉപയോഗിക്കാനാവും.

Hooks ഒരു JavaScript ഫങ്ഷൻ ആണു, പക്ഷെ അവ ഉപയോഗിക്കുന്നതിനായി 2 നിയമങ്ങൾ ശ്രദ്ധിക്കേണ്ടതുണ്ട്. ഈ നിയമങ്ങൾ പാലിക്കപ്പെടാൻ സഹായിക്കുന്നതിനായി ഞങ്ങൾ linter plugin ഉം ഉൾപ്പെടുത്തിയിട്ടുണ്ട്.

Hooks-നെ ടോപ്പ് ലെവെൽ കമ്പോണന്റിൽ നിന്ന് മാത്രം വിളിക്കുക

Hooks-നെ ലൂപ്കളിൽ നിന്നോ, കണ്ടീഷനുകളിൽ നിന്നോ Nested ഫങ്ഷനുകളിൽ നിന്നൊ വിളിക്കാൻ പാടുള്ളതല്ല. പകരം React Hooks എപ്പോഴും ഫങ്ഷന്റെ ടോപ്പ് ലെവലിൽ മാത്രം എഴുതുക. ഇത് വഴി , ഓരോ തവണ കമ്പോണന്റ് റെന്റര്‍ ചെയ്യുമ്പോളും ഒരേ ക്രമത്തിൽ ആണ് വിളിക്കപ്പെടുന്നത് എന്ന് നമുക്ക് ഉറപ്പ് വരുത്താം. അങ്ങനെ React നു ഒന്നിലധികം useStateനും useEffectനും ഫങ്ഷൻ കോളുകളിൽ ഉൾപ്പടെ Hooks ന്റെ അവസ്ഥ ശെരിയായ രീതിയിൽ നിലനിർത്താൻ കഴിയുന്നു. (നിങ്ങൾക്ക് കൂടുതൽ അറിയാൻ താൽപര്യം ഉണ്ടെങ്കിൽ ഞങ്ങൾ ഇതിനെ പറ്റി കൂടുതൽ ഗഹനമായി താഴെ പറയുന്നുണ്ട്.)

Hooks-നെ React ഫങ്ഷനിൽ നിന്ന് മാത്രം വിളിക്കുക

Hooks-നെ സാധാരണ JavaScript ഫങ്ഷനിൽ നിന്ന് വിളിക്കാൻ പാടുള്ളതല്ല. പകരം,

  • ✅ Hooks-നെ React ഫങ്ഷൻ കമ്പോണന്റിൽ നിന്ന് വിളിക്കാം.
  • ✅ നിങ്ങൾ ഇഷ്ടാനുസരണം നിർമ്മിച്ചെടുത്ത Hooks(Custom Hook) ഇൽ നിന്നും വിളിക്കാം (അവയെ കുറിച്ച് നമ്മൾ അടുത്ത പേജിൽ പഠിക്കും ).

ഈ നിയമങ്ങൾ പാലിക്കുക വഴി ഒരു React കമ്പോണന്റിലെ സ്‍റ്റേറ്റ്ഫുൾ ലോജിക് ശെരിയായ രീതിയിൽ തന്നെയാണ് നിങ്ങളുടെ കോഡിലുള്ളതെന്ന് ഉറപ്പിക്കാം.

ESLint Plugin

ഈ 2 നിയമങ്ങളും പാലിക്കപ്പെടുന്നു എന്ന് ഉറപ്പ് വരുത്തുന്നതിനായി eslint-plugin-react-hooks എന്നൊരു പ്ലഗ്ഗിൻ ഞങ്ങൾ പുറത്തിറക്കിയിട്ടുണ്ട്. ഇത് നിങ്ങളുടെ പ്രോജെക്റ്റിൽ ഉൾപ്പെടുത്തി ഉപയോഗിച്ച് നോക്കാവുന്നതാണ്:

npm install eslint-plugin-react-hooks --save-dev
// Your ESLint configuration
{
  "plugins": [
    // ...
    "react-hooks"
  ],
  "rules": {
    // ...
    "react-hooks/rules-of-hooks": "error", // Checks rules of Hooks
    "react-hooks/exhaustive-deps": "warn" // Checks effect dependencies
  }
}

ഭാവിയിൽ Create React App ലും മറ്റു ടൂൾകിറ്റുകളിലും ഡീഫോൾട്ട് ആയി ഉൾപെടുത്താൻ ഞങ്ങൾ ഉദ്ദേശിക്കുന്നുണ്ട്.

അടുത്ത പേജിൽ നിങ്ങളുടെ ഇഷ്ടാനുസരണമുള്ള Hooks എങ്ങനെ നിർമ്മിച്ചെടുക്കാം എന്ന് വിശദീകരിക്കുന്നുണ്ട്. ഈ പേജിൽ ഈ 2 നിയമങ്ങൾ പിന്തുടരേണ്ടതിന്റെ ആവശ്യകത വിശദീകരിക്കുന്നതാണ്.

വിശദീകരണം

നേരത്തെ നമ്മൾ പഠിച്ചത് പോലെ ഒരു കമ്പോണന്റിൽ ഒന്നിലധികം state ഉം effects ഉം ഉപയോഗിക്കാൻ സാധിക്കുന്നതാണ്:

function Form() {
  // 1. Use the name state variable
  const [name, setName] = useState('Mary');

  // 2. Use an effect for persisting the form
  useEffect(function persistForm() {
    localStorage.setItem('formData', name);
  });

  // 3. Use the surname state variable
  const [surname, setSurname] = useState('Poppins');

  // 4. Use an effect for updating the title
  useEffect(function updateTitle() {
    document.title = name + ' ' + surname;
  });

  // ...
}

useState കോളുകളും അതിന്റെ അനുബന്ധമായ state ഉം React എങ്ങനെ മനസ്സിലാക്കും ? ഉത്തരം ഇതാണ് , Hooks വിളിക്കപ്പെടുന്നതിന്റെ ക്രമത്തിന്റെ അടിസ്ഥാനത്തിലാണ്. എല്ലാ റെന്ററുകളിലും Hook-കളെ വിളിക്കുന്നത് ഒരേ ക്രമത്തിൽ ആയതിനാൽ നമ്മുടെ ഉദാഹരണ പ്രോഗ്രാം ശെരിയായി പ്രവർത്തിക്കുന്നു :

// ------------
// First render
// ------------
useState('Mary')           // 1. Initialize the name state variable with 'Mary'
useEffect(persistForm)     // 2. Add an effect for persisting the form
useState('Poppins')        // 3. Initialize the surname state variable with 'Poppins'
useEffect(updateTitle)     // 4. Add an effect for updating the title

// -------------
// Second render
// -------------
useState('Mary')           // 1. Read the name state variable (argument is ignored)
useEffect(persistForm)     // 2. Replace the effect for persisting the form
useState('Poppins')        // 3. Read the surname state variable (argument is ignored)
useEffect(updateTitle)     // 4. Replace the effect for updating the title

// ...

എല്ലാ റെന്ററുകളിലും Hooks വിളിക്കപ്പെടുന്നത് ഒരേ ക്രമത്തിൽ ആയിരിക്കുന്നിടത്തോളം, React നു ശെരിയായ രീതിയിൽ state കളെ അനുബന്ധിക്കാൻ കഴിയുന്നു.മറിച്ച് ഒരു കണ്ടീഷനു അകത്തു നിന്ന് Hook-നെ വിളിച്ചാലോ (ഉദാഹരണമായി persistForm എഫെക്റ്റ് ) ?

  // 🔴 We're breaking the first rule by using a Hook in a condition
  if (name !== '') {
    useEffect(function persistForm() {
      localStorage.setItem('formData', name);
    });
  }

ആദ്യത്തെ റെന്ററിൽ name !== '' കണ്ടീഷൻ true ആണ്, ഫലമായി Hook റൺ ആയി. യൂസർ, ഫോം ക്ലിയർ ചെയ്താൽ അടുത്ത റെന്ററിൽ കണ്ടീഷൻ false ആയിരിക്കും , ഫലമായി ആ Hook വർക്ക് ചെയ്യാതിരിക്കുന്നു. അങ്ങനെ Hook-നെ വിളിക്കുന്ന ക്രമം വ്യത്യസ്തമാകുന്നു:

useState('Mary')           // 1. Read the name state variable (argument is ignored)
// useEffect(persistForm)  // 🔴 This Hook was skipped!
useState('Poppins')        // 🔴 2 (but was 3). Fail to read the surname state variable
useEffect(updateTitle)     // 🔴 3 (but was 4). Fail to replace the effect

ഇപ്പോൾ രണ്ടാമത്തെ useState കോളിന് എന്താണ് റിട്ടേൺ നൽകേണ്ടത് എന്ന് React നു അറിയില്ല. React കരുതുന്നത്, രണ്ടാമത് വിളിക്കുന്നത് persistForm നോട് അനുബന്ധമായ state ആണെന്നാണ്, ഇതിനു മുമ്പുള്ള റെന്ററിൽ സംഭവിച്ചത് പോലെ. പക്ഷെ അതല്ല സംഭവിക്കുന്നത്. പിന്നീടങ്ങോട്ട് റൺ ചെയ്യാതെ പോയ Hook-നു ശേഷമുള്ള Hook-കൾ വിളിക്കപ്പെടുന്നു, തൽഫലമായി ബഗുകൾ ഉണ്ടാകുന്നു.

അത്കൊണ്ട് Hook-നെ എപ്പോഴും കമ്പോണന്റിന്റെ ടോപ്പ് ലെവലിൽ നിന്ന് മാത്രം വിളിക്കുക. ഒരു കണ്ടീഷനെ അടിസ്ഥാനമാക്കി Hook വിളിക്കപ്പെടണം എങ്കിൽ , Hook-ന്റെ അകത്ത് കണ്ടീഷൻ നൽകാവുന്നതാണ്:

  useEffect(function persistForm() {
    // 👍 We're not breaking the first rule anymore
    if (name !== '') {
      localStorage.setItem('formData', name);
    }
  });

ഈ ലിൻറ്റ് റൂൾ ഉപയോക്കുവനെങ്കിൽ ഈ പ്രെഷ്നത്തേ കുറിച്ച് ആശങ്കപ്പെടേണ്ടതില്ല. എന്ത്കൊണ്ട് Hooks ഇങ്ങനെ പ്രവർത്തിക്കുന്നു എന്നും ഏതൊക്കെ പ്രശ്നങ്ങളെയാണ് ഈ നിയമങ്ങൾ ശെരിയായി നേരിടുന്നത് എന്നും മനസ്സിലായല്ലോ.

അടുത്ത ഘട്ടങ്ങൾ

ഇനി നമുക്ക് Custom Hook! എഴുതാൻ പഠിക്കാവുന്നതണ്. Custom Hook-ഇൽ React ന്റെ തനത് Hook-കൾ ഉപയോഗിക്കാവുന്നതാണ്, ഈ കസ്റ്റം Hook വിവിധ കമ്പോണന്റുകളിൽ ഉപയോഗപ്പെടുത്തവുന്നതുമണ്.