മലയാളം

വിപ്ലവകരമായ റിയാക്ട് `use` ഹുക്കിനെക്കുറിച്ചുള്ള സമഗ്രമായ ഗൈഡ്. പ്രോമിസുകളും കോൺടെക്സ്റ്റും കൈകാര്യം ചെയ്യുന്നതിലുള്ള ഇതിന്റെ സ്വാധീനം, റിസോഴ്സ് ഉപയോഗം, പ്രകടനം, മികച്ച രീതികൾ എന്നിവ ആഗോള ഡെവലപ്പർമാർക്കായി ആഴത്തിൽ വിശകലനം ചെയ്യുന്നു.

റിയാക്ടിന്റെ `use` ഹുക്ക്: പ്രോമിസുകൾ, കോൺടെക്സ്റ്റ്, റിസോഴ്സ് മാനേജ്മെന്റ് എന്നിവയുടെ ആഴത്തിലുള്ള വിശകലനം

റിയാക്ടിന്റെ ലോകം നിരന്തരം വികസിച്ചുകൊണ്ടിരിക്കുകയാണ്, ഇത് ഡെവലപ്പർമാരുടെ അനുഭവം മെച്ചപ്പെടുത്തുകയും വെബിൽ സാധ്യമായതിന്റെ അതിരുകൾ ഭേദിക്കുകയും ചെയ്യുന്നു. ക്ലാസുകളിൽ നിന്ന് ഹുക്കുകളിലേക്കുള്ള ഓരോ പ്രധാന മാറ്റവും നമ്മൾ യൂസർ ഇന്റർഫേസുകൾ നിർമ്മിക്കുന്ന രീതിയെ അടിസ്ഥാനപരമായി മാറ്റിമറിച്ചു. ഇന്ന്, അത്തരത്തിലുള്ള മറ്റൊരു പരിവർത്തനത്തിന്റെ വക്കിലാണ് നമ്മൾ നിൽക്കുന്നത്, ലളിതമെന്ന് തോന്നിക്കുന്ന ഒരു ഫംഗ്ഷൻ ഇതിന് വഴിയൊരുക്കുന്നു: `use` ഹുക്ക്.

വർഷങ്ങളായി, ഡെവലപ്പർമാർ അസിൻക്രണസ് ഓപ്പറേഷനുകളുടെയും സ്റ്റേറ്റ് മാനേജ്മെന്റിന്റെയും സങ്കീർണ്ണതകളുമായി മല്ലിടുകയായിരുന്നു. ഡാറ്റ ലഭ്യമാക്കുന്നത് പലപ്പോഴും `useEffect`, `useState`, ലോഡിംഗ്/എറർ സ്റ്റേറ്റുകൾ എന്നിവയുടെ ഒരു കെട്ടുപിണഞ്ഞ വലയായിരുന്നു. കോൺടെക്സ്റ്റ് ഉപയോഗിക്കുന്നത് ശക്തമായിരുന്നെങ്കിലും, എല്ലാ ഉപഭോക്താക്കളിലും റീ-റെൻഡറുകൾക്ക് കാരണമാകുന്ന ഒരു പ്രധാന പ്രകടന പ്രശ്നവും അതിനുണ്ടായിരുന്നു. ഈ ദീർഘകാല വെല്ലുവിളികൾക്ക് റിയാക്ടിന്റെ മനോഹരമായ ഉത്തരമാണ് `use` ഹുക്ക്.

ഈ സമഗ്രമായ ഗൈഡ് ലോകമെമ്പാടുമുള്ള പ്രൊഫഷണൽ റിയാക്ട് ഡെവലപ്പർമാർക്കായി രൂപകൽപ്പന ചെയ്തിട്ടുള്ളതാണ്. നമ്മൾ `use` ഹുക്കിനെക്കുറിച്ച് ആഴത്തിൽ പഠിക്കുകയും, അതിന്റെ പ്രവർത്തനരീതികൾ വിശകലനം ചെയ്യുകയും, അതിന്റെ രണ്ട് പ്രധാന പ്രാരംഭ ഉപയോഗങ്ങളായ പ്രോമിസുകൾ അൺറാപ്പ് ചെയ്യുന്നതും കോൺടെക്സ്റ്റിൽ നിന്ന് വായിക്കുന്നതും പര്യവേക്ഷണം ചെയ്യുകയും ചെയ്യും. അതിലും പ്രധാനമായി, റിസോഴ്സ് ഉപയോഗം, പ്രകടനം, ആപ്ലിക്കേഷൻ ആർക്കിടെക്ചർ എന്നിവയിൽ ഇത് ചെലുത്തുന്ന അഗാധമായ സ്വാധീനത്തെക്കുറിച്ച് നമ്മൾ വിശകലനം ചെയ്യും. നിങ്ങളുടെ റിയാക്ട് ആപ്ലിക്കേഷനുകളിൽ അസിൻക് ലോജിക്കും സ്റ്റേറ്റും കൈകാര്യം ചെയ്യുന്ന രീതിയെക്കുറിച്ച് പുനർവിചിന്തനം ചെയ്യാൻ തയ്യാറാകുക.

ഒരു അടിസ്ഥാനപരമായ മാറ്റം: എന്താണ് `use` ഹുക്കിനെ വ്യത്യസ്തമാക്കുന്നത്?

പ്രോമിസുകളിലേക്കും കോൺടെക്സ്റ്റിലേക്കും കടക്കുന്നതിന് മുമ്പ്, എന്തുകൊണ്ടാണ് `use` ഇത്ര വിപ്ലവകരമായതെന്ന് മനസ്സിലാക്കേണ്ടത് പ്രധാനമാണ്. വർഷങ്ങളായി, റിയാക്ട് ഡെവലപ്പർമാർ കർശനമായ ഹുക്ക് നിയമങ്ങൾ (Rules of Hooks) പാലിച്ചാണ് പ്രവർത്തിച്ചിരുന്നത്:

ഓരോ റെൻഡറിംഗിലും സ്ഥിരമായ ഒരു കോൾ ഓർഡർ നിലനിർത്തുന്നതിനെ ആശ്രയിച്ചാണ് `useState`, `useEffect` പോലുള്ള പരമ്പരാഗത ഹുക്കുകൾ പ്രവർത്തിക്കുന്നത്. `use` ഹുക്ക് ഈ കീഴ്വഴക്കത്തെ തകർക്കുന്നു. നിങ്ങൾക്ക് `use` ഹുക്കിനെ കണ്ടീഷനുകൾക്കുള്ളിൽ (`if`/`else`), ലൂപ്പുകൾക്കുള്ളിൽ (`for`/`map`), എന്തിന്, നേരത്തെയുള്ള `return` സ്റ്റേറ്റ്‌മെന്റുകളിൽ പോലും വിളിക്കാൻ കഴിയും.

ഇതൊരു ചെറിയ മാറ്റമല്ല; ഇതൊരു മാതൃകാപരമായ മാറ്റമാണ്. ഇത് റിസോഴ്‌സുകൾ ഉപയോഗിക്കുന്നതിന് കൂടുതൽ വഴക്കമുള്ളതും സ്വാഭാവികവുമായ ഒരു മാർഗ്ഗം നൽകുന്നു. സ്റ്റാറ്റിക്, ടോപ്-ലെവൽ സബ്‌സ്‌ക്രിപ്‌ഷൻ മോഡലിൽ നിന്ന് ഡൈനാമിക്, ഓൺ-ഡിമാൻഡ് കൺസംപ്ഷൻ മോഡലിലേക്ക് ഇത് മാറുന്നു. സൈദ്ധാന്തികമായി ഇത് വിവിധതരം റിസോഴ്‌സുകളിൽ പ്രവർത്തിക്കുമെങ്കിലും, ഇതിന്റെ പ്രാരംഭ ഘട്ടത്തിലുള്ള ഉപയോഗം റിയാക്ട് ഡെവലപ്‌മെന്റിലെ ഏറ്റവും സാധാരണമായ രണ്ട് പ്രശ്‌നങ്ങളായ പ്രോമിസുകളിലും കോൺടെക്സ്റ്റിലുമാണ് ശ്രദ്ധ കേന്ദ്രീകരിക്കുന്നത്.

അടിസ്ഥാന ആശയം: മൂല്യങ്ങൾ അൺറാപ്പ് ചെയ്യുക

അടിസ്ഥാനപരമായി, `use` ഹുക്ക് ഒരു റിസോഴ്സിൽ നിന്ന് ഒരു മൂല്യം "അൺറാപ്പ്" ചെയ്യാൻ രൂപകൽപ്പന ചെയ്തിട്ടുള്ളതാണ്. ഇതിനെ ഇങ്ങനെ ചിന്തിക്കുക:

ഈ രണ്ട് ശക്തമായ കഴിവുകളും നമുക്ക് വിശദമായി പരിശോധിക്കാം.

അസിൻക്രണസ് ഓപ്പറേഷനുകളിൽ വൈദഗ്ദ്ധ്യം നേടാം: പ്രോമിസുകൾക്കൊപ്പം `use`

ആധുനിക വെബ് ആപ്ലിക്കേഷനുകളുടെ ജീവനാഡിയാണ് ഡാറ്റാ ഫെച്ചിംഗ്. റിയാക്ടിലെ പരമ്പരാഗത രീതി പ്രവർത്തനക്ഷമമായിരുന്നെങ്കിലും, പലപ്പോഴും അത് ദൈർഘ്യമേറിയതും സൂക്ഷ്മമായ ബഗ്ഗുകൾക്ക് സാധ്യതയുള്ളതുമായിരുന്നു.

പഴയ രീതി: `useEffect`, `useState` എന്നിവയുടെ കളി

ഉപയോക്താവിന്റെ ഡാറ്റ ലഭ്യമാക്കുന്ന ഒരു ലളിതമായ കമ്പോണന്റ് പരിഗണിക്കുക. സാധാരണ രീതി ഏകദേശം ഇങ്ങനെയായിരിക്കും:


import React, { useState, useEffect } from 'react';

function UserProfile({ userId }) {
  const [user, setUser] = useState(null);
  const [isLoading, setIsLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    let isMounted = true;
    const fetchUser = async () => {
      try {
        setIsLoading(true);
        const response = await fetch(`https://api.example.com/users/${userId}`);
        if (!response.ok) {
          throw new Error('Network response was not ok');
        }
        const data = await response.json();
        if (isMounted) {
          setUser(data);
        }
      } catch (err) {
        if (isMounted) {
          setError(err);
        }
      } finally {
        if (isMounted) {
          setIsLoading(false);
        }
      }
    };

    fetchUser();

    return () => {
      isMounted = false;
    };
  }, [userId]);

  if (isLoading) {
    return <p>പ്രൊഫൈൽ ലോഡ് ചെയ്യുന്നു...</p>;
  }

  if (error) {
    return <p>പിശക്: {error.message}</p>;
  }

  return (
    <div>
      <h1>{user.name}</h1>
      <p>ഇമെയിൽ: {user.email}</p>
    </div>
  );
}

ഈ കോഡിൽ ധാരാളം ബോയിലർപ്ലേറ്റ് ഉണ്ട്. നമ്മൾ മൂന്ന് വ്യത്യസ്ത സ്റ്റേറ്റുകൾ (`user`, `isLoading`, `error`) സ്വമേധയാ കൈകാര്യം ചെയ്യണം, കൂടാതെ ഒരു മൗണ്ടഡ് ഫ്ലാഗ് ഉപയോഗിച്ച് റേസ് കണ്ടീഷനുകളെക്കുറിച്ചും ക്ലീനപ്പിനെക്കുറിച്ചും ശ്രദ്ധാലുവായിരിക്കണം. കസ്റ്റം ഹുക്കുകൾക്ക് ഇത് ലളിതമാക്കാൻ കഴിയുമെങ്കിലും, അടിസ്ഥാനപരമായ സങ്കീർണ്ണത നിലനിൽക്കുന്നു.

പുതിയ രീതി: `use` ഉപയോഗിച്ച് മനോഹരമായ അസിൻക്രണസിറ്റി

`use` ഹുക്ക്, റിയാക്ട് സസ്പെൻസുമായി ചേർന്ന്, ഈ മുഴുവൻ പ്രക്രിയയെയും നാടകീയമായി ലളിതമാക്കുന്നു. സിൻക്രണസ് കോഡ് പോലെ വായിക്കാൻ കഴിയുന്ന അസിൻക്രണസ് കോഡ് എഴുതാൻ ഇത് നമ്മെ അനുവദിക്കുന്നു.

ഇതേ കമ്പോണന്റ് `use` ഉപയോഗിച്ച് എങ്ങനെ എഴുതാമെന്ന് നോക്കാം:


// ഈ കമ്പോണന്റ് നിങ്ങൾ <Suspense>, <ErrorBoundary> എന്നിവയിൽ പൊതിയണം
import { use } from 'react';
import { fetchUser } from './api'; // ഇത് ഒരു കാഷ് ചെയ്ത പ്രോമിസ് തിരികെ നൽകുന്നു എന്ന് കരുതുക

function UserProfile({ userId }) {
  // പ്രോമിസ് റിസോൾവ് ആകുന്നതുവരെ `use` കമ്പോണന്റിനെ സസ്പെൻഡ് ചെയ്യും
  const user = use(fetchUser(userId));

  // എക്സിക്യൂഷൻ ഇവിടെ എത്തുമ്പോൾ, പ്രോമിസ് റിസോൾവ് ആകുകയും `user`-ൽ ഡാറ്റ ഉണ്ടാകുകയും ചെയ്യും.
  // കമ്പോണന്റിൽ isLoading അല്ലെങ്കിൽ error സ്റ്റേറ്റുകളുടെ ആവശ്യമില്ല.
  return (
    <div>
      <h1>{user.name}</h1>
      <p>ഇമെയിൽ: {user.email}</p>
    </div>
  );
}

വ്യത്യാസം വളരെ വലുതാണ്. ലോഡിംഗ്, എറർ സ്റ്റേറ്റുകൾ നമ്മുടെ കമ്പോണന്റ് ലോജിക്കിൽ നിന്ന് അപ്രത്യക്ഷമായി. എന്താണ് പിന്നണിയിൽ സംഭവിക്കുന്നത്?

  1. `UserProfile` ആദ്യമായി റെൻഡർ ചെയ്യുമ്പോൾ, അത് `use(fetchUser(userId))` വിളിക്കുന്നു.
  2. `fetchUser` ഫംഗ്ഷൻ ഒരു നെറ്റ്‌വർക്ക് അഭ്യർത്ഥന ആരംഭിച്ച് ഒരു പ്രോമിസ് തിരികെ നൽകുന്നു.
  3. `use` ഹുക്ക് ഈ പെൻഡിംഗ് പ്രോമിസ് സ്വീകരിക്കുകയും ഈ കമ്പോണന്റിന്റെ റെൻഡറിംഗ് സസ്പെൻഡ് ചെയ്യാൻ റിയാക്ടിന്റെ റെൻഡററുമായി ആശയവിനിമയം നടത്തുകയും ചെയ്യുന്നു.
  4. റിയാക്ട് കമ്പോണന്റ് ട്രീയിലൂടെ മുകളിലേക്ക് പോയി ഏറ്റവും അടുത്തുള്ള `` ബൗണ്ടറി കണ്ടെത്തുകയും അതിന്റെ `fallback` UI (ഉദാഹരണത്തിന്, ഒരു സ്പിന്നർ) പ്രദർശിപ്പിക്കുകയും ചെയ്യുന്നു.
  5. പ്രോമിസ് റിസോൾവ് ചെയ്തുകഴിഞ്ഞാൽ, റിയാക്ട് `UserProfile` വീണ്ടും റെൻഡർ ചെയ്യുന്നു. ഇത്തവണ, അതേ പ്രോമിസുമായി `use` വിളിക്കുമ്പോൾ, പ്രോമിസിന് റിസോൾവ് ചെയ്ത ഒരു മൂല്യമുണ്ട്. `use` ഈ മൂല്യം തിരികെ നൽകുന്നു.
  6. കമ്പോണന്റ് റെൻഡറിംഗ് തുടരുന്നു, ഉപയോക്താവിന്റെ പ്രൊഫൈൽ പ്രദർശിപ്പിക്കുന്നു.
  7. പ്രോമിസ് റിജെക്ട് ചെയ്താൽ, `use` എറർ ത്രോ ചെയ്യുന്നു. റിയാക്ട് ഇത് പിടിച്ചെടുക്കുകയും ട്രീയിലൂടെ മുകളിലേക്ക് പോയി ഏറ്റവും അടുത്തുള്ള ``-യിൽ ഒരു ഫാൾബാക്ക് എറർ UI പ്രദർശിപ്പിക്കുകയും ചെയ്യുന്നു.

റിസോഴ്സ് ഉപയോഗം: കാഷിംഗിന്റെ പ്രാധാന്യം

`use(fetchUser(userId))` എന്നതിന്റെ ലാളിത്യം ഒരു നിർണ്ണായകമായ വിശദാംശം മറച്ചുവെക്കുന്നു: ഓരോ റെൻഡറിലും നിങ്ങൾ ഒരു പുതിയ പ്രോമിസ് ഉണ്ടാക്കരുത്. നമ്മുടെ `fetchUser` ഫംഗ്ഷൻ `() => fetch(...)` എന്നായിരുന്നെങ്കിൽ, നമ്മൾ അത് കമ്പോണന്റിനുള്ളിൽ നേരിട്ട് വിളിച്ചാൽ, ഓരോ റെൻഡർ ശ്രമത്തിലും ഒരു പുതിയ നെറ്റ്‌വർക്ക് അഭ്യർത്ഥന ഉണ്ടാകും, ഇത് അനന്തമായ ഒരു ലൂപ്പിലേക്ക് നയിക്കും. കമ്പോണന്റ് സസ്പെൻഡ് ചെയ്യും, പ്രോമിസ് റിസോൾവ് ചെയ്യും, റിയാക്ട് വീണ്ടും റെൻഡർ ചെയ്യും, ഒരു പുതിയ പ്രോമിസ് ഉണ്ടാകും, വീണ്ടും സസ്പെൻഡ് ചെയ്യും.

പ്രോമിസുകൾക്കൊപ്പം `use` ഉപയോഗിക്കുമ്പോൾ മനസ്സിലാക്കേണ്ട ഏറ്റവും പ്രധാനപ്പെട്ട റിസോഴ്സ് മാനേജ്മെന്റ് ആശയം ഇതാണ്. പ്രോമിസ് സ്ഥിരതയുള്ളതും റീ-റെൻഡറുകളിലുടനീളം കാഷ് ചെയ്തതുമായിരിക്കണം.

ഇതിന് സഹായിക്കുന്നതിന് റിയാക്ട് ഒരു പുതിയ `cache` ഫംഗ്ഷൻ നൽകുന്നു. നമുക്ക് ഒരു ശക്തമായ ഡാറ്റാ-ഫെച്ചിംഗ് യൂട്ടിലിറ്റി ഉണ്ടാക്കാം:


// api.js
import { cache } from 'react';

export const fetchUser = cache(async (userId) => {
  console.log(`ഉപയോക്താവിനായി ഡാറ്റ ലഭ്യമാക്കുന്നു: ${userId}`);
  const response = await fetch(`https://api.example.com/users/${userId}`);
  if (!response.ok) {
    throw new Error('ഉപയോക്താവിന്റെ ഡാറ്റ ലഭ്യമാക്കുന്നതിൽ പരാജയപ്പെട്ടു.');
  }
  return response.json();
});

റിയാക്ടിൽ നിന്നുള്ള `cache` ഫംഗ്ഷൻ അസിൻക്രണസ് ഫംഗ്ഷനെ മെമ്മോയിസ് ചെയ്യുന്നു. `fetchUser(1)` വിളിക്കുമ്പോൾ, അത് ഫെച്ച് ആരംഭിച്ച് തത്ഫലമായുണ്ടാകുന്ന പ്രോമിസ് സംഭരിക്കുന്നു. മറ്റൊരു കമ്പോണന്റ് (അല്ലെങ്കിൽ അതേ കമ്പോണന്റ് തുടർന്നുള്ള റെൻഡറിൽ) അതേ റെൻഡർ പാസിനുള്ളിൽ `fetchUser(1)` വീണ്ടും വിളിച്ചാൽ, `cache` അതേ പ്രോമിസ് ഒബ്ജക്റ്റ് തന്നെ തിരികെ നൽകും, അനാവശ്യ നെറ്റ്‌വർക്ക് അഭ്യർത്ഥനകൾ തടയുന്നു. ഇത് ഡാറ്റാ ഫെച്ചിംഗിനെ ഐഡംപൊട്ടന്റ് ആക്കുകയും `use` ഹുക്കിനൊപ്പം ഉപയോഗിക്കാൻ സുരക്ഷിതമാക്കുകയും ചെയ്യുന്നു.

ഇത് റിസോഴ്സ് മാനേജ്മെന്റിലെ ഒരു അടിസ്ഥാനപരമായ മാറ്റമാണ്. കമ്പോണന്റിനുള്ളിൽ ഫെച്ച് സ്റ്റേറ്റ് കൈകാര്യം ചെയ്യുന്നതിനുപകരം, നമ്മൾ റിസോഴ്സ് (ഡാറ്റാ പ്രോമിസ്) അതിന് പുറത്ത് കൈകാര്യം ചെയ്യുന്നു, കമ്പോണന്റ് അത് ഉപയോഗിക്കുക മാത്രം ചെയ്യുന്നു.

സ്റ്റേറ്റ് മാനേജ്മെന്റിൽ വിപ്ലവം: കോൺടെക്സ്റ്റിനൊപ്പം `use`

"പ്രോപ്പ് ഡ്രില്ലിംഗ്" ഒഴിവാക്കുന്നതിനുള്ള ശക്തമായ ഒരു ഉപകരണമാണ് റിയാക്ട് കോൺടെക്സ്റ്റ്—അതായത്, പല ലെയറുകളിലുള്ള കമ്പോണന്റുകളിലൂടെ പ്രോപ്പുകൾ താഴേക്ക് കൈമാറുന്നത് ഒഴിവാക്കാൻ. എന്നിരുന്നാലും, ഇതിന്റെ പരമ്പരാഗതമായ ഉപയോഗത്തിന് ഒരു പ്രധാന പ്രകടന പോരായ്മയുണ്ട്.

`useContext` പ്രതിസന്ധി

`useContext` ഹുക്ക് ഒരു കമ്പോണന്റിനെ ഒരു കോൺടെക്സ്റ്റിലേക്ക് സബ്സ്ക്രൈബ് ചെയ്യുന്നു. ഇതിനർത്ഥം, കോൺടെക്സ്റ്റിന്റെ മൂല്യം എപ്പോൾ മാറിയാലും, ആ കോൺടെക്സ്റ്റിനായി `useContext` ഉപയോഗിക്കുന്ന ഓരോ കമ്പോണന്റും റീ-റെൻഡർ ചെയ്യും. കമ്പോണന്റ് കോൺടെക്സ്റ്റ് മൂല്യത്തിലെ മാറ്റമില്ലാത്ത ഒരു ചെറിയ ഭാഗം മാത്രമേ ശ്രദ്ധിക്കുന്നുള്ളൂവെങ്കിലും ഇത് ശരിയാണ്.

ഉപയോക്താവിന്റെ വിവരങ്ങളും നിലവിലെ തീമും സൂക്ഷിക്കുന്ന ഒരു `SessionContext` പരിഗണിക്കുക:


// SessionContext.js
const SessionContext = createContext({
  user: null,
  theme: 'light',
  updateTheme: () => {},
});

// ഉപയോക്താവിനെ മാത്രം ശ്രദ്ധിക്കുന്ന കമ്പോണന്റ്
function WelcomeMessage() {
  const { user } = useContext(SessionContext);
  console.log('Rendering WelcomeMessage');
  return <p>സ്വാഗതം, {user?.name}!</p>;
}

// തീമിനെ മാത്രം ശ്രദ്ധിക്കുന്ന കമ്പോണന്റ്
function ThemeToggleButton() {
  const { theme, updateTheme } = useContext(SessionContext);
  console.log('Rendering ThemeToggleButton');
  return <button onClick={updateTheme}>{theme === 'light' ? 'dark' : 'light'} തീമിലേക്ക് മാറുക</button>;
}

ഈ സാഹചര്യത്തിൽ, ഉപയോക്താവ് `ThemeToggleButton` ക്ലിക്ക് ചെയ്യുകയും `updateTheme` വിളിക്കപ്പെടുകയും ചെയ്യുമ്പോൾ, മുഴുവൻ `SessionContext` മൂല്യവും മാറ്റിസ്ഥാപിക്കപ്പെടുന്നു. ഇത് `user` ഒബ്ജക്റ്റിന് മാറ്റമൊന്നും സംഭവിച്ചിട്ടില്ലെങ്കിലും, `ThemeToggleButton`, `WelcomeMessage` എന്നീ രണ്ട് കമ്പോണന്റുകളും റീ-റെൻഡർ ചെയ്യാൻ കാരണമാകുന്നു. നൂറുകണക്കിന് കോൺടെക്സ്റ്റ് ഉപഭോക്താക്കളുള്ള ഒരു വലിയ ആപ്ലിക്കേഷനിൽ ഇത് ഗുരുതരമായ പ്രകടന പ്രശ്നങ്ങൾക്ക് ഇടയാക്കും.

`use(Context)` അവതരിക്കുന്നു: കണ്ടീഷണൽ കൺസംപ്ഷൻ

`use` ഹുക്ക് ഈ പ്രശ്നത്തിന് ഒരു വഴിത്തിരിവായ പരിഹാരം നൽകുന്നു. ഇത് കണ്ടീഷണലായി വിളിക്കാൻ കഴിയുന്നതുകൊണ്ട്, ഒരു കമ്പോണന്റ് യഥാർത്ഥത്തിൽ മൂല്യം വായിക്കുമ്പോൾ മാത്രം കോൺടെക്സ്റ്റിലേക്ക് ഒരു സബ്സ്ക്രിപ്ഷൻ സ്ഥാപിക്കുന്നു.

ഈ ശക്തി പ്രകടമാക്കാൻ നമുക്ക് ഒരു കമ്പോണന്റ് റീഫാക്ടർ ചെയ്യാം:


function UserSettings({ userId }) {
  const { user, theme } = useContext(SessionContext); // പരമ്പരാഗത രീതി: എപ്പോഴും സബ്സ്ക്രൈബ് ചെയ്യുന്നു

  // നിലവിൽ ലോഗിൻ ചെയ്ത ഉപയോക്താവിനായി മാത്രം തീം ക്രമീകരണങ്ങൾ കാണിക്കുന്നു എന്ന് കരുതുക
  if (user?.id !== userId) {
    return <p>നിങ്ങൾക്ക് നിങ്ങളുടെ സ്വന്തം ക്രമീകരണങ്ങൾ മാത്രമേ കാണാൻ കഴിയൂ.</p>;
  }

  // ഉപയോക്തൃ ഐഡി പൊരുത്തപ്പെട്ടാൽ മാത്രം ഈ ഭാഗം പ്രവർത്തിക്കുന്നു
  return <div>നിലവിലെ തീം: {theme}</div>;
}

`useContext` ഉപയോഗിച്ച്, ഈ `UserSettings` കമ്പോണന്റ് തീം മാറുമ്പോഴെല്ലാം റീ-റെൻഡർ ചെയ്യും, `user.id !== userId` ആണെങ്കിൽ പോലും, തീം വിവരങ്ങൾ ഒരിക്കലും പ്രദർശിപ്പിക്കപ്പെടുന്നില്ലെങ്കിലും. സബ്സ്ക്രിപ്ഷൻ ടോപ്-ലെവലിൽ നിരുപാധികം സ്ഥാപിക്കപ്പെടുന്നു.

ഇനി, `use` പതിപ്പ് നോക്കാം:


import { use } from 'react';

function UserSettings({ userId }) {
  // ആദ്യം ഉപയോക്താവിനെ വായിക്കുക. ഈ ഭാഗം ചെലവുകുറഞ്ഞതോ ആവശ്യമുള്ളതോ ആണെന്ന് കരുതുക.
  const user = use(SessionContext).user;

  // കണ്ടീഷൻ പാലിക്കുന്നില്ലെങ്കിൽ, നമ്മൾ നേരത്തെ മടങ്ങുന്നു.
  // പ്രധാനമായി, നമ്മൾ ഇതുവരെ തീം വായിച്ചിട്ടില്ല.
  if (user?.id !== userId) {
    return <p>നിങ്ങൾക്ക് നിങ്ങളുടെ സ്വന്തം ക്രമീകരണങ്ങൾ മാത്രമേ കാണാൻ കഴിയൂ.</p>;
  }

  // കണ്ടീഷൻ പാലിച്ചാൽ മാത്രം, നമ്മൾ കോൺടെക്സ്റ്റിൽ നിന്ന് തീം വായിക്കുന്നു.
  // കോൺടെക്സ്റ്റ് മാറ്റങ്ങളിലേക്കുള്ള സബ്സ്ക്രിപ്ഷൻ ഇവിടെ, കണ്ടീഷണലായി സ്ഥാപിക്കപ്പെടുന്നു.
  const theme = use(SessionContext).theme;

  return <div>നിലവിലെ തീം: {theme}</div>;
}

ഇതൊരു ഗെയിം ചേഞ്ചറാണ്. ഈ പതിപ്പിൽ, `user.id`, `userId` എന്നിവ പൊരുത്തപ്പെടുന്നില്ലെങ്കിൽ, കമ്പോണന്റ് നേരത്തെ മടങ്ങുന്നു. `const theme = use(SessionContext).theme;` എന്ന ലൈൻ ഒരിക്കലും പ്രവർത്തിക്കുന്നില്ല. അതിനാൽ, ഈ കമ്പോണന്റ് ഇൻസ്റ്റൻസ് `SessionContext`-ലേക്ക് സബ്സ്ക്രൈബ് ചെയ്യുന്നില്ല. ആപ്പിൽ മറ്റെവിടെയെങ്കിലും തീം മാറ്റിയാൽ, ഈ കമ്പോണന്റ് അനാവശ്യമായി റീ-റെൻഡർ ചെയ്യില്ല. കോൺടെക്സ്റ്റിൽ നിന്ന് കണ്ടീഷണലായി വായിക്കുന്നതിലൂടെ അത് അതിന്റെ റിസോഴ്സ് ഉപയോഗം ഫലപ്രദമായി ഒപ്റ്റിമൈസ് ചെയ്തു.

റിസോഴ്സ് ഉപയോഗ വിശകലനം: സബ്സ്ക്രിപ്ഷൻ മോഡലുകൾ

കോൺടെക്സ്റ്റ് ഉപയോഗിക്കുന്നതിനുള്ള മാനസിക മാതൃക നാടകീയമായി മാറുന്നു:

റീ-റെൻഡറുകളിലുള്ള ഈ സൂക്ഷ്മമായ നിയന്ത്രണം വലിയ തോതിലുള്ള ആപ്ലിക്കേഷനുകളിൽ പ്രകടനം മെച്ചപ്പെടുത്തുന്നതിനുള്ള ഒരു ശക്തമായ ഉപകരണമാണ്. ഇത് ഡെവലപ്പർമാർക്ക് അപ്രസക്തമായ സ്റ്റേറ്റ് അപ്‌ഡേറ്റുകളിൽ നിന്ന് യഥാർത്ഥത്തിൽ ഒറ്റപ്പെട്ട കമ്പോണന്റുകൾ നിർമ്മിക്കാൻ അനുവദിക്കുന്നു, ഇത് സങ്കീർണ്ണമായ മെമ്മോയിസേഷൻ (`React.memo`) അല്ലെങ്കിൽ സ്റ്റേറ്റ് സെലക്ടർ പാറ്റേണുകൾ ഉപയോഗിക്കാതെ തന്നെ കൂടുതൽ കാര്യക്ഷമവും പ്രതികരണശേഷിയുള്ളതുമായ യൂസർ ഇന്റർഫേസിലേക്ക് നയിക്കുന്നു.

ഇവയുടെ സംയോജനം: കോൺടെക്സ്റ്റിലെ പ്രോമിസുകളോടൊപ്പം `use`

ഈ രണ്ട് ആശയങ്ങളും സംയോജിപ്പിക്കുമ്പോഴാണ് `use`-ന്റെ യഥാർത്ഥ ശക്തി വ്യക്തമാകുന്നത്. ഒരു കോൺടെക്സ്റ്റ് പ്രൊവൈഡർ ഡാറ്റ നേരിട്ട് നൽകാതെ, ആ ഡാറ്റയ്ക്കുള്ള ഒരു പ്രോമിസ് നൽകിയാലോ? ആപ്പ്-വൈഡ് ഡാറ്റാ സ്രോതസ്സുകൾ കൈകാര്യം ചെയ്യുന്നതിന് ഈ പാറ്റേൺ അവിശ്വസനീയമാംവിധം ഉപയോഗപ്രദമാണ്.


// DataContext.js
import { createContext } from 'react';
import { fetchSomeGlobalData } from './api'; // കാഷ് ചെയ്ത ഒരു പ്രോമിസ് തിരികെ നൽകുന്നു

// കോൺടെക്സ്റ്റ് ഡാറ്റയല്ല, ഒരു പ്രോമിസാണ് നൽകുന്നത്.
export const GlobalDataContext = createContext(fetchSomeGlobalData());

// App.js
function App() {
  return (
    <GlobalDataContext.Provider value={fetchSomeGlobalData()}>
      <Suspense fallback={<h1>ആപ്ലിക്കേഷൻ ലോഡ് ചെയ്യുന്നു...</h1>}>
        <Dashboard />
      </Suspense>
    </GlobalDataContext.Provider>
  );
}

// Dashboard.js
import { use } from 'react';
import { GlobalDataContext } from './DataContext';

function Dashboard() {
  // ആദ്യത്തെ `use` കോൺടെക്സ്റ്റിൽ നിന്ന് പ്രോമിസ് വായിക്കുന്നു.
  const dataPromise = use(GlobalDataContext);

  // രണ്ടാമത്തെ `use` പ്രോമിസിനെ അൺറാപ്പ് ചെയ്യുന്നു, ആവശ്യമെങ്കിൽ സസ്പെൻഡ് ചെയ്യുന്നു.
  const globalData = use(dataPromise);

  // മുകളിലുള്ള രണ്ട് വരികൾ എഴുതാനുള്ള കൂടുതൽ സംക്ഷിപ്തമായ മാർഗ്ഗം:
  // const globalData = use(use(GlobalDataContext));

  return <h1>സ്വാഗതം, {globalData.userName}!</h1>;
}

നമുക്ക് `const globalData = use(use(GlobalDataContext));` എന്നതിനെ വിശകലനം ചെയ്യാം:

  1. `use(GlobalDataContext)`: ഉള്ളിലെ കോൾ ആദ്യം പ്രവർത്തിക്കുന്നു. ഇത് `GlobalDataContext`-ൽ നിന്ന് മൂല്യം വായിക്കുന്നു. നമ്മുടെ ക്രമീകരണത്തിൽ, ഈ മൂല്യം `fetchSomeGlobalData()` തിരികെ നൽകുന്ന ഒരു പ്രോമിസാണ്.
  2. `use(dataPromise)`: പുറത്തുള്ള കോൾ ഈ പ്രോമിസ് സ്വീകരിക്കുന്നു. ഇത് നമ്മൾ ആദ്യ ഭാഗത്ത് കണ്ടതുപോലെ തന്നെ പ്രവർത്തിക്കുന്നു: പ്രോമിസ് പെൻഡിംഗ് ആണെങ്കിൽ `Dashboard` കമ്പോണന്റിനെ സസ്പെൻഡ് ചെയ്യുന്നു, റിജെക്ട് ചെയ്താൽ എറർ ത്രോ ചെയ്യുന്നു, അല്ലെങ്കിൽ റിസോൾവ് ചെയ്ത ഡാറ്റ തിരികെ നൽകുന്നു.

ഈ പാറ്റേൺ അസാധാരണമാംവിധം ശക്തമാണ്. ഇത് ഡാറ്റാ-ഫെച്ചിംഗ് ലോജിക്കിനെ ഡാറ്റ ഉപയോഗിക്കുന്ന കമ്പോണന്റുകളിൽ നിന്ന് വേർതിരിക്കുന്നു, അതേസമയം തടസ്സമില്ലാത്ത ലോഡിംഗ് അനുഭവത്തിനായി റിയാക്ടിന്റെ ബിൽറ്റ്-ഇൻ സസ്പെൻസ് മെക്കാനിസം പ്രയോജനപ്പെടുത്തുന്നു. ഡാറ്റ എങ്ങനെ അല്ലെങ്കിൽ എപ്പോൾ ലഭ്യമാക്കുന്നു എന്ന് കമ്പോണന്റുകൾ അറിയേണ്ടതില്ല; അവർ അതിനായി ആവശ്യപ്പെടുന്നു, ബാക്കിയുള്ളവ റിയാക്ട് ക്രമീകരിക്കുന്നു.

പ്രകടനം, അപകടങ്ങൾ, മികച്ച രീതികൾ

ഏതൊരു ശക്തമായ ഉപകരണത്തെയും പോലെ, `use` ഹുക്ക് ഫലപ്രദമായി ഉപയോഗിക്കുന്നതിന് ധാരണയും അച്ചടക്കവും ആവശ്യമാണ്. പ്രൊഡക്ഷൻ ആപ്ലിക്കേഷനുകൾക്കുള്ള ചില പ്രധാന പരിഗണനകൾ ഇതാ.

പ്രകടന സംഗ്രഹം

ഒഴിവാക്കേണ്ട സാധാരണ അപകടങ്ങൾ

  1. കാഷ് ചെയ്യാത്ത പ്രോമിസുകൾ: ഒന്നാമത്തെ തെറ്റ്. ഒരു കമ്പോണന്റിൽ നേരിട്ട് `use(fetch(...))` വിളിക്കുന്നത് ഒരു അനന്തമായ ലൂപ്പിന് കാരണമാകും. എല്ലായ്പ്പോഴും റിയാക്ടിന്റെ `cache` അല്ലെങ്കിൽ SWR/React Query പോലുള്ള ലൈബ്രറികൾ പോലുള്ള ഒരു കാഷിംഗ് മെക്കാനിസം ഉപയോഗിക്കുക.
  2. ബൗണ്ടറികളുടെ അഭാവം: ഒരു പാരന്റ് `` ബൗണ്ടറിയില്ലാതെ `use(Promise)` ഉപയോഗിക്കുന്നത് നിങ്ങളുടെ ആപ്ലിക്കേഷൻ ക്രാഷ് ആക്കും. അതുപോലെ, ഒരു പാരന്റ് `` ഇല്ലാതെ റിജെക്ട് ചെയ്ത പ്രോമിസും ആപ്പ് ക്രാഷ് ആക്കും. ഈ ബൗണ്ടറികൾ മനസ്സിൽ വെച്ചുകൊണ്ട് നിങ്ങളുടെ കമ്പോണന്റ് ട്രീ രൂപകൽപ്പന ചെയ്യണം.
  3. അകാലത്തിലുള്ള ഒപ്റ്റിമൈസേഷൻ: പ്രകടനത്തിന് `use(Context)` മികച്ചതാണെങ്കിലും, അത് എല്ലായ്പ്പോഴും ആവശ്യമില്ല. ലളിതവും, അപൂർവ്വമായി മാറുന്നതും, അല്ലെങ്കിൽ ഉപഭോക്താക്കൾ റീ-റെൻഡർ ചെയ്യാൻ ചെലവുകുറഞ്ഞതുമായ കോൺടെക്സ്റ്റുകൾക്ക്, പരമ്പരാഗത `useContext` തികച്ചും അനുയോജ്യവും അല്പംകൂടി ലളിതവുമാണ്. വ്യക്തമായ പ്രകടന കാരണമില്ലാതെ നിങ്ങളുടെ കോഡ് സങ്കീർണ്ണമാക്കരുത്.
  4. `cache`-നെ തെറ്റിദ്ധരിക്കുന്നത്: റിയാക്ടിന്റെ `cache` ഫംഗ്ഷൻ അതിന്റെ ആർഗ്യുമെന്റുകളെ അടിസ്ഥാനമാക്കി മെമ്മോയിസ് ചെയ്യുന്നു, എന്നാൽ ഈ കാഷ് സാധാരണയായി സെർവർ അഭ്യർത്ഥനകൾക്കിടയിലോ ക്ലയന്റിലെ ഒരു ഫുൾ പേജ് റീലോഡിലോ മായ്ച്ചുകളയുന്നു. ഇത് റിക്വസ്റ്റ്-ലെവൽ കാഷിംഗിനാണ് രൂപകൽപ്പന ചെയ്തിരിക്കുന്നത്, ദീർഘകാല ക്ലയന്റ്-സൈഡ് സ്റ്റേറ്റിനല്ല. സങ്കീർണ്ണമായ ക്ലയന്റ്-സൈഡ് കാഷിംഗ്, ഇൻവാലിഡേഷൻ, മ്യൂട്ടേഷൻ എന്നിവയ്ക്കായി, ഒരു സമർപ്പിത ഡാറ്റാ-ഫെച്ചിംഗ് ലൈബ്രറി ഇപ്പോഴും വളരെ ശക്തമായ ഒരു തിരഞ്ഞെടുപ്പാണ്.

മികച്ച രീതികളുടെ ചെക്ക്‌ലിസ്റ്റ്

ഭാവി `use` ആണ്: സെർവർ കമ്പോണന്റുകളും അതിനപ്പുറവും

`use` ഹുക്ക് ഒരു ക്ലയന്റ്-സൈഡ് സൗകര്യം മാത്രമല്ല; ഇത് റിയാക്ട് സെർവർ കമ്പോണന്റ്സിന്റെ (RSCs) ഒരു അടിസ്ഥാന സ്തംഭമാണ്. ഒരു RSC എൻവയോൺമെന്റിൽ, ഒരു കമ്പോണന്റ് സെർവറിൽ പ്രവർത്തിക്കാൻ കഴിയും. അത് `use(fetch(...))` വിളിക്കുമ്പോൾ, സെർവറിന് ആ കമ്പോണന്റിന്റെ റെൻഡറിംഗ് അക്ഷരാർത്ഥത്തിൽ താൽക്കാലികമായി നിർത്താനും, ഡാറ്റാബേസ് ക്വറിയോ API കോളോ പൂർത്തിയാകുന്നതുവരെ കാത്തിരിക്കാനും, തുടർന്ന് ഡാറ്റയുമായി റെൻഡറിംഗ് പുനരാരംഭിക്കാനും, അന്തിമ HTML ക്ലയന്റിലേക്ക് സ്ട്രീം ചെയ്യാനും കഴിയും.

ഇത് ഒരു തടസ്സമില്ലാത്ത മാതൃക സൃഷ്ടിക്കുന്നു, അവിടെ ഡാറ്റാ ഫെച്ചിംഗ് റെൻഡറിംഗ് പ്രക്രിയയുടെ ഒരു ഫസ്റ്റ്-ക്ലാസ് സിറ്റിസൺ ആകുന്നു, സെർവർ-സൈഡ് ഡാറ്റാ വീണ്ടെടുക്കലും ക്ലയന്റ്-സൈഡ് UI കോമ്പോസിഷനും തമ്മിലുള്ള അതിർത്തി മായ്ച്ചുകളയുന്നു. നമ്മൾ നേരത്തെ എഴുതിയ അതേ `UserProfile` കമ്പോണന്റിന്, കുറഞ്ഞ മാറ്റങ്ങളോടെ, സെർവറിൽ പ്രവർത്തിക്കാനും, അതിന്റെ ഡാറ്റ ലഭ്യമാക്കാനും, പൂർണ്ണരൂപത്തിലുള്ള HTML ബ്രൗസറിലേക്ക് അയയ്ക്കാനും കഴിയും, ഇത് വേഗതയേറിയ പ്രാരംഭ പേജ് ലോഡുകളിലേക്കും മികച്ച ഉപയോക്തൃ അനുഭവത്തിലേക്കും നയിക്കുന്നു.

`use` API വികസിപ്പിക്കാവുന്നതുമാണ്. ഭാവിയിൽ, ഇത് ഒബ്സർവബിൾസ് (ഉദാഹരണത്തിന്, RxJS-ൽ നിന്ന്) അല്ലെങ്കിൽ മറ്റ് കസ്റ്റം "thenable" ഒബ്ജക്റ്റുകൾ പോലുള്ള മറ്റ് അസിൻക്രണസ് സ്രോതസ്സുകളിൽ നിന്ന് മൂല്യങ്ങൾ അൺറാപ്പ് ചെയ്യാൻ ഉപയോഗിക്കാം, ഇത് റിയാക്ട് കമ്പോണന്റുകൾ ബാഹ്യ ഡാറ്റയും ഇവന്റുകളുമായി എങ്ങനെ സംവദിക്കുന്നു എന്നതിനെ കൂടുതൽ ഏകീകരിക്കുന്നു.

ഉപസംഹാരം: റിയാക്ട് ഡെവലപ്‌മെന്റിന്റെ ഒരു പുതിയ യുഗം

`use` ഹുക്ക് ഒരു പുതിയ API എന്നതിലുപരി; വൃത്തിയുള്ളതും, കൂടുതൽ ഡിക്ലറേറ്റീവും, കൂടുതൽ പ്രകടനക്ഷമവുമായ റിയാക്ട് ആപ്ലിക്കേഷനുകൾ എഴുതാനുള്ള ഒരു ക്ഷണമാണിത്. അസിൻക്രണസ് ഓപ്പറേഷനുകളും കോൺടെക്സ്റ്റ് ഉപയോഗവും റെൻഡറിംഗ് ഫ്ലോയിലേക്ക് നേരിട്ട് സംയോജിപ്പിക്കുന്നതിലൂടെ, വർഷങ്ങളായി സങ്കീർണ്ണമായ പാറ്റേണുകളും ബോയിലർപ്ലേറ്റുകളും ആവശ്യമായിരുന്ന പ്രശ്നങ്ങൾ ഇത് ഭംഗിയായി പരിഹരിക്കുന്നു.

ഓരോ ആഗോള ഡെവലപ്പർക്കുമുള്ള പ്രധാന പാഠങ്ങൾ ഇവയാണ്:

റിയാക്ട് 19-ന്റെയും അതിനപ്പുറമുള്ള യുഗത്തിലേക്കും നമ്മൾ നീങ്ങുമ്പോൾ, `use` ഹുക്കിൽ വൈദഗ്ദ്ധ്യം നേടുന്നത് അത്യാവശ്യമായിരിക്കും. ഇത് ഡൈനാമിക് യൂസർ ഇന്റർഫേസുകൾ നിർമ്മിക്കുന്നതിനുള്ള കൂടുതൽ അവബോധജന്യവും ശക്തവുമായ ഒരു മാർഗ്ഗം തുറക്കുന്നു, ക്ലയന്റും സെർവറും തമ്മിലുള്ള വിടവ് നികത്തുകയും അടുത്ത തലമുറ വെബ് ആപ്ലിക്കേഷനുകൾക്ക് വഴിയൊരുക്കുകയും ചെയ്യുന്നു.

`use` ഹുക്കിനെക്കുറിച്ചുള്ള നിങ്ങളുടെ ചിന്തകൾ എന്തൊക്കെയാണ്? നിങ്ങൾ ഇത് ഉപയോഗിച്ച് പരീക്ഷണം ആരംഭിച്ചോ? നിങ്ങളുടെ അനുഭവങ്ങളും ചോദ്യങ്ങളും ഉൾക്കാഴ്ചകളും താഴെ കമന്റുകളിൽ പങ്കുവെക്കൂ!