Custom Hook-களுடன் லாஜிக்கைக் மீண்டும் பயன்படுத்துதல்

React-இல் useState, useContext, useEffect போன்ற பல உள்ளமைந்த Hook-கள் உள்ளன. சில நேரங்களில், இன்னும் குறிப்பிட்ட நோக்கத்திற்கான ஒரு Hook இருந்தால் நன்றாக இருக்கும் என்று தோன்றலாம்: உதாரணமாக, தரவை fetch செய்வது, பயனர் ஆன்லைனில் உள்ளாரா என்பதை கண்காணிப்பது, அல்லது chat room-க்கு இணைப்பது. இத்தகைய Hook-களை React-இல் நீங்கள் காணாமல் போகலாம், ஆனால் உங்கள் application-ன் தேவைகளுக்காக நீங்களே Hook-களை உருவாக்கலாம்.

நீங்கள் கற்றுக்கொள்ள போவது

  • custom Hook-கள் என்றால் என்ன, உங்களுடையதை எப்படி எழுதுவது
  • component-களுக்கிடையே logic-ஐ எப்படி மீண்டும் பயன்படுத்துவது
  • உங்கள் custom Hook-களை எப்படி பெயரிட்டு அமைப்பது
  • custom Hook-களை எப்போது, ஏன் பிரித்தெடுக்க வேண்டும்

Custom Hook-கள்: component-களுக்கிடையே logic-ஐப் பகிர்தல்

நீங்கள் network-ஐ அதிகமாக நம்பும் ஒரு app-ஐ உருவாக்குகிறீர்கள் என்று நினைத்துப் பாருங்கள் (பெரும்பாலான app-கள் அப்படித்தான்). உங்கள் app-ஐப் பயன்படுத்திக்கொண்டிருக்கும்போது பயனரின் network connection தவறுதலாக துண்டிக்கப்பட்டால், அவரை எச்சரிக்க விரும்புகிறீர்கள். இதை எப்படி செய்வீர்கள்? உங்கள் component-இல் இரண்டு விஷயங்கள் தேவைப்படுவது போல தெரிகிறது:

  1. network online-இல் உள்ளதா என்பதை கண்காணிக்கும் ஒரு state பகுதி.
  2. உலகளாவிய online மற்றும் offline event-களுக்கு subscribe செய்து, அந்த state-ஐ புதுப்பிக்கும் ஒரு Effect.

இது உங்கள் component-ஐ network status உடன் ஒத்திசைவாக வைத்திருக்கும். நீங்கள் இதுபோன்ற ஒன்றில் தொடங்கலாம்:

import { useState, useEffect } from 'react';

export default function StatusBar() {
  const [isOnline, setIsOnline] = useState(true);
  useEffect(() => {
    function handleOnline() {
      setIsOnline(true);
    }
    function handleOffline() {
      setIsOnline(false);
    }
    window.addEventListener('online', handleOnline);
    window.addEventListener('offline', handleOffline);
    return () => {
      window.removeEventListener('online', handleOnline);
      window.removeEventListener('offline', handleOffline);
    };
  }, []);

  return <h1>{isOnline ? '✅ ஆன்லைன்' : '❌ துண்டிக்கப்பட்டது'}</h1>;
}

உங்கள் network-ஐ on மற்றும் off செய்து பாருங்கள்; உங்கள் செயல்களுக்கு பதிலாக இந்த StatusBar எப்படி புதுப்பிக்கப்படுகிறது என்பதை கவனியுங்கள்.

இப்போது இதே logic-ஐ வேறு component-இலும் பயன்படுத்த விரும்புகிறீர்கள் என்று நினைத்துப் பாருங்கள். network off ஆக இருக்கும்போது disabled ஆகி, “சேமி” என்பதற்குப் பதிலாக “மீண்டும் இணைக்கிறது…” என்று காட்டும் Save button-ஐ உருவாக்க விரும்புகிறீர்கள்.

தொடங்க, isOnline state-ஐயும் Effect-ஐயும் SaveButton-க்குள் copy-paste செய்யலாம்:

import { useState, useEffect } from 'react';

export default function SaveButton() {
  const [isOnline, setIsOnline] = useState(true);
  useEffect(() => {
    function handleOnline() {
      setIsOnline(true);
    }
    function handleOffline() {
      setIsOnline(false);
    }
    window.addEventListener('online', handleOnline);
    window.addEventListener('offline', handleOffline);
    return () => {
      window.removeEventListener('online', handleOnline);
      window.removeEventListener('offline', handleOffline);
    };
  }, []);

  function handleSaveClick() {
    console.log('✅ முன்னேற்றம் சேமிக்கப்பட்டது');
  }

  return (
    <button disabled={!isOnline} onClick={handleSaveClick}>
      {isOnline ? 'முன்னேற்றத்தைச் சேமி' : 'மீண்டும் இணைக்கிறது...'}
    </button>
  );
}

network-ஐ off செய்தால் button-ன் தோற்றம் மாறுமா என்பதைச் சரிபாருங்கள்.

இந்த இரண்டு component-களும் நன்றாக வேலை செய்கின்றன, ஆனால் அவற்றுக்கிடையே logic மீண்டும் மீண்டும் இருப்பது நல்லதல்ல. அவற்றின் காட்சி தோற்றம் வேறுபட்டிருந்தாலும், அவற்றுக்கிடையே logic-ஐ மீண்டும் பயன்படுத்த விரும்புகிறீர்கள்.

ஒரு component-இலிருந்து உங்கள் சொந்த custom Hook-ஐப் பிரித்தெடுத்தல்

useState மற்றும் useEffect போல, உள்ளமைந்த useOnlineStatus Hook இருந்தது என்று ஒரு நிமிடம் நினைத்துப் பாருங்கள். அப்போது இந்த இரண்டு component-களையும் தெளிவுப்படுத்தி, அவற்றுக்கிடையிலான மீளுருவாக்கத்தை நீக்கலாம்:

function StatusBar() {
const isOnline = useOnlineStatus();
return <h1>{isOnline ? '✅ ஆன்லைன்' : '❌ துண்டிக்கப்பட்டது'}</h1>;
}

function SaveButton() {
const isOnline = useOnlineStatus();

function handleSaveClick() {
console.log('✅ முன்னேற்றம் சேமிக்கப்பட்டது');
}

return (
<button disabled={!isOnline} onClick={handleSaveClick}>
{isOnline ? 'முன்னேற்றத்தைச் சேமி' : 'மீண்டும் இணைக்கிறது...'}
</button>
);
}

அப்படியான உள்ளமைந்த Hook இல்லை என்றாலும், அதை நீங்களே எழுதலாம். useOnlineStatus என்ற function-ஐ அறிவித்து, முன்பு எழுதிய component-களிலிருந்த மீண்டும் வரும் code அனைத்தையும் அதற்குள் நகர்த்துங்கள்:

function useOnlineStatus() {
const [isOnline, setIsOnline] = useState(true);
useEffect(() => {
function handleOnline() {
setIsOnline(true);
}
function handleOffline() {
setIsOnline(false);
}
window.addEventListener('online', handleOnline);
window.addEventListener('offline', handleOffline);
return () => {
window.removeEventListener('online', handleOnline);
window.removeEventListener('offline', handleOffline);
};
}, []);
return isOnline;
}

function-ன் முடிவில் isOnline-ஐ return செய்யுங்கள். இதனால் உங்கள் component-கள் அந்த மதிப்பைப் படிக்க முடியும்:

import { useOnlineStatus } from './useOnlineStatus.js';

function StatusBar() {
  const isOnline = useOnlineStatus();
  return <h1>{isOnline ? '✅ ஆன்லைன்' : '❌ துண்டிக்கப்பட்டது'}</h1>;
}

function SaveButton() {
  const isOnline = useOnlineStatus();

  function handleSaveClick() {
    console.log('✅ முன்னேற்றம் சேமிக்கப்பட்டது');
  }

  return (
    <button disabled={!isOnline} onClick={handleSaveClick}>
      {isOnline ? 'முன்னேற்றத்தைச் சேமி' : 'மீண்டும் இணைக்கிறது...'}
    </button>
  );
}

export default function App() {
  return (
    <>
      <SaveButton />
      <StatusBar />
    </>
  );
}

network-ஐ on/off செய்தால் இரண்டு component-களும் புதுப்பிக்கப்படுகின்றனவா என்பதைச் சரிபாருங்கள்.

இப்போது உங்கள் component-களில் அதிகமான மீண்டும் வரும் logic இல்லை. இன்னும் முக்கியமாக, அவற்றுக்குள் இருக்கும் code, எப்படி செய்வது (browser event-களுக்கு subscribe செய்வது) என்பதைக் காட்டாமல், எதைச் செய்ய விரும்புகின்றன (online status-ஐப் பயன்படுத்துதல்!) என்பதை விவரிக்கிறது.

logic-ஐ custom Hook-களுக்குள் பிரித்தெடுத்தால், ஒரு external system அல்லது browser API-யுடன் நீங்கள் எப்படி செயல்படுகிறீர்கள் என்ற சிக்கலான விவரங்களை மறைக்கலாம். உங்கள் component-களின் code உங்கள் நோக்கத்தை வெளிப்படுத்தும்; implementation-ஐ அல்ல.

Hook பெயர்கள் எப்போதும் use-ஆல் தொடங்கும்

React application-கள் component-களிலிருந்து கட்டப்படுகின்றன. component-கள் உள்ளமைந்தவையாக இருந்தாலும் custom ஆனவையாக இருந்தாலும் Hook-களிலிருந்து கட்டப்படுகின்றன. மற்றவர்கள் உருவாக்கிய custom Hook-களை நீங்கள் அடிக்கடி பயன்படுத்தலாம்; ஆனால் சில சமயம் ஒன்றை நீங்களே எழுத வேண்டி வரலாம்!

இந்த பெயரிடும் மரபுகளை நீங்கள் பின்பற்ற வேண்டும்:

  1. React component பெயர்கள் capital letter-ஆல் தொடங்க வேண்டும், StatusBar மற்றும் SaveButton போல. React component-கள் JSX துண்டு போன்ற, React காட்டத் தெரிந்த ஏதாவது ஒன்றையும் return செய்ய வேண்டும்.
  2. Hook பெயர்கள் use-க்கு பின் capital letter வருமாறு தொடங்க வேண்டும், useState (உள்ளமைந்தது) அல்லது useOnlineStatus (இந்தப் பக்கத்தில் முன்பு பார்த்த custom Hook) போல. Hook-கள் எந்த மதிப்பையும் return செய்யலாம்.

இந்த மரபு, ஒரு component-ஐ பார்த்தவுடன் அதன் state, Effect-கள் மற்றும் பிற React அம்சங்கள் எங்கு “மறைந்திருக்கலாம்” என்பதை எப்போதும் அறிய உதவுகிறது. உதாரணமாக, உங்கள் component-க்குள் getColor() function call-ஐ பார்த்தால், அதன் பெயர் use-ஆல் தொடங்காததால் அதற்குள் React state இருக்க முடியாது என்பதில் உறுதியாக இருக்கலாம். ஆனால் useOnlineStatus() போன்ற function call-க்குள் பெரும்பாலும் பிற Hook call-கள் இருக்கும்!

Note

உங்கள் linter React-க்காக configure செய்யப்பட்டிருந்தால், அது இந்த பெயரிடும் மரபை கட்டாயப்படுத்தும். மேலுள்ள sandbox-க்கு scroll செய்து, useOnlineStatus-ஐ getOnlineStatus என rename செய்து பாருங்கள். அதன் பிறகு அதற்குள் useState அல்லது useEffect-ஐ call செய்ய linter அனுமதிக்காது என்பதை கவனியுங்கள். Hook-களும் component-களும் மட்டுமே பிற Hook-களை call செய்ய முடியும்!

Deep Dive

rendering நடக்கும் போது call செய்யப்படும் எல்லா function-களும் use prefix-ஆல் தொடங்க வேண்டுமா?

இல்லை. Hook-களை call செய்யாத function-கள் Hook-களாக இருக்க தேவையில்லை.

உங்கள் function எந்த Hook-களையும் call செய்யவில்லை என்றால், use prefix-ஐ தவிருங்கள். அதற்கு பதிலாக, use prefix இல்லாத சாதாரண function-ஆக எழுதுங்கள். உதாரணமாக, கீழே உள்ள useSorted Hook-களை call செய்யவில்லை, எனவே அதற்கு பதிலாக getSorted என்று அழையுங்கள்:

// 🔴 Avoid: A Hook that doesn't use Hooks
function useSorted(items) {
return items.slice().sort();
}

// ✅ Good: A regular function that doesn't use Hooks
function getSorted(items) {
return items.slice().sort();
}

இதனால் உங்கள் code இந்த சாதாரண function-ஐ conditions உட்பட எங்கும் call செய்ய முடியும்:

function List({ items, shouldSort }) {
let displayedItems = items;
if (shouldSort) {
// ✅ It's ok to call getSorted() conditionally because it's not a Hook
displayedItems = getSorted(items);
}
// ...
}

ஒரு function அதன் உள்ளே குறைந்தது ஒரு Hook-ஐயாவது பயன்படுத்தினால், அதற்கு use prefix கொடுத்து (அதனால் அதை Hook ஆக்கி) எழுத வேண்டும்:

// ✅ Good: A Hook that uses other Hooks
function useAuth() {
return useContext(Auth);
}

தொழில்நுட்ப ரீதியாக, இதை React கட்டாயப்படுத்தாது. கொள்கை ரீதியாக, பிற Hook-களை call செய்யாத Hook-ஐ நீங்கள் உருவாக்கலாம். இது பெரும்பாலும் குழப்பமாகவும் கட்டுப்பாடாகவும் இருக்கும், எனவே அந்த pattern-ஐ தவிர்ப்பது நல்லது. ஆனால் அரிதாக அது பயனுள்ளதாக இருக்கும் சூழல்கள் இருக்கலாம். உதாரணமாக, உங்கள் function இப்போது எந்த Hook-களையும் பயன்படுத்தாமல் இருக்கலாம், ஆனால் எதிர்காலத்தில் அதில் சில Hook call-களை சேர்க்க திட்டமிட்டிருக்கலாம். அப்போது அதற்கு use prefix உடன் பெயரிடுவது பொருத்தமாகும்:

// ✅ Good: A Hook that will likely use some other Hooks later
function useAuth() {
// TODO: Replace with this line when authentication is implemented:
// return useContext(Auth);
return TEST_USER;
}

அப்போது component-கள் அதை conditionally call செய்ய முடியாது. நீங்கள் உண்மையில் அதற்குள் Hook call-களை சேர்க்கும்போது இது முக்கியமாகும். அதற்குள் Hook-களைப் பயன்படுத்த திட்டமில்லையெனில் (இப்போது அல்லது பின்னர்), அதை Hook ஆக்க வேண்டாம்.

Custom Hook-கள் stateful logic-ஐப் பகிர உதவும்; state-ஐயே அல்ல

முன்னைய உதாரணத்தில், network-ஐ on/off செய்தபோது இரண்டு component-களும் ஒன்றாக புதுப்பிக்கப்பட்டன. ஆனால் ஒரே isOnline state variable அவற்றுக்கிடையே பகிரப்படுகிறது என்று நினைப்பது தவறு. இந்த code-ஐ பாருங்கள்:

function StatusBar() {
const isOnline = useOnlineStatus();
// ...
}

function SaveButton() {
const isOnline = useOnlineStatus();
// ...
}

மீண்டும் வரும் logic-ஐ பிரித்தெடுக்கும் முன் இருந்தது போலவே இது வேலை செய்கிறது:

function StatusBar() {
const [isOnline, setIsOnline] = useState(true);
useEffect(() => {
// ...
}, []);
// ...
}

function SaveButton() {
const [isOnline, setIsOnline] = useState(true);
useEffect(() => {
// ...
}, []);
// ...
}

இவை இரண்டு முற்றிலும் தனித்தனி state variable-களும் Effect-களும்! நீங்கள் அவற்றை ஒரே external value-உடன் (network on-ஆக உள்ளதா) ஒத்திசைத்ததால், அவை ஒரே நேரத்தில் ஒரே மதிப்பைக் கொண்டிருந்தன.

இதைக் கூடுதல் தெளிவாக காட்ட, வேறு உதாரணம் தேவை. இந்த Form component-ஐ கருதுங்கள்:

import { useState } from 'react';

export default function Form() {
  const [firstName, setFirstName] = useState('Mary');
  const [lastName, setLastName] = useState('Poppins');

  function handleFirstNameChange(e) {
    setFirstName(e.target.value);
  }

  function handleLastNameChange(e) {
    setLastName(e.target.value);
  }

  return (
    <>
      <label>
        முதல் பெயர்:
        <input value={firstName} onChange={handleFirstNameChange} />
      </label>
      <label>
        கடைசி பெயர்:
        <input value={lastName} onChange={handleLastNameChange} />
      </label>
      <p><b>காலை வணக்கம், {firstName} {lastName}.</b></p>
    </>
  );
}

ஒவ்வொரு form field-க்கும் சில மீண்டும் வரும் logic உள்ளது:

  1. state-ன் ஒரு பகுதி உள்ளது (firstName மற்றும் lastName).
  2. change handler ஒன்று உள்ளது (handleFirstNameChange மற்றும் handleLastNameChange).
  3. அந்த input-க்கான value மற்றும் onChange attribute-களை குறிப்பிடும் JSX துண்டு உள்ளது.

மீண்டும் வரும் logic-ஐ இந்த useFormInput custom Hook-க்குள் பிரித்தெடுக்கலாம்:

import { useState } from 'react';

export function useFormInput(initialValue) {
  const [value, setValue] = useState(initialValue);

  function handleChange(e) {
    setValue(e.target.value);
  }

  const inputProps = {
    value: value,
    onChange: handleChange
  };

  return inputProps;
}

இது value எனப்படும் ஒரே ஒரு state variable-ஐ மட்டுமே அறிவிப்பதை கவனியுங்கள்.

ஆனால் Form component useFormInput-ஐ இரண்டு முறை call செய்கிறது:

function Form() {
const firstNameProps = useFormInput('Mary');
const lastNameProps = useFormInput('Poppins');
// ...

இதனால் இது இரண்டு தனித்தனி state variable-களை அறிவித்தது போல வேலை செய்கிறது!

Custom Hook-கள் stateful logic-ஐப் பகிர உதவும்; ஆனால் state-ஐயே பகிராது. ஒரே Hook-க்கு செய்யப்படும் ஒவ்வொரு call-மும், அதே Hook-க்கு செய்யப்படும் மற்ற எல்லா call-களிலிருந்தும் முற்றிலும் தனித்தனி. அதனால் மேலுள்ள இரண்டு sandbox-களும் முழுமையாக சமமானவை. விரும்பினால், மேலே scroll செய்து அவற்றை ஒப்பிடுங்கள். custom Hook-ஐ பிரித்தெடுக்கும் முன் மற்றும் பின் நடத்தை ஒன்றுதான்.

பல component-களுக்கிடையே state-ஐயே பகிர வேண்டுமெனில், அதற்கு பதிலாக அதை மேலே தூக்கி கீழே pass செய்யுங்கள்.

Hook-களுக்கிடையே reactive மதிப்புகளை pass செய்தல்

உங்கள் custom Hook-களுக்குள் உள்ள code, உங்கள் component ஒவ்வொரு re-render-க்கும் மீண்டும் இயங்கும். அதனால் component-களைப் போலவே, custom Hook-களும் pure ஆக இருக்க வேண்டும். custom Hook-களின் code-ஐ உங்கள் component body-ன் ஒரு பகுதியாக நினைத்துக்கொள்ளுங்கள்!

custom Hook-கள் உங்கள் component-உடன் சேர்ந்து re-render ஆகுவதால், அவை எப்போதும் சமீபத்திய props மற்றும் state-ஐ பெறும். இதன் அர்த்தம் என்ன என்பதைப் பார்க்க, இந்த chat room உதாரணத்தை கருதுங்கள். server URL அல்லது chat room-ஐ மாற்றிப் பாருங்கள்:

import { useState, useEffect } from 'react';
import { createConnection } from './chat.js';
import { showNotification } from './notifications.js';

export default function ChatRoom({ roomId }) {
  const [serverUrl, setServerUrl] = useState('https://localhost:1234');

  useEffect(() => {
    const options = {
      serverUrl: serverUrl,
      roomId: roomId
    };
    const connection = createConnection(options);
    connection.on('message', (msg) => {
      showNotification('புதிய செய்தி: ' + msg);
    });
    connection.connect();
    return () => connection.disconnect();
  }, [roomId, serverUrl]);

  return (
    <>
      <label>
        சர்வர் URL:
        <input value={serverUrl} onChange={e => setServerUrl(e.target.value)} />
      </label>
      <h1>{roomId} அறைக்கு வரவேற்கிறோம்!</h1>
    </>
  );
}

நீங்கள் serverUrl அல்லது roomId-ஐ மாற்றும்போது, Effect உங்கள் மாற்றங்களுக்கு “react” செய்து மீண்டும் ஒத்திசைகிறது. உங்கள் Effect-ன் dependency-களை மாற்றும் ஒவ்வொரு முறையும் chat மீண்டும் இணைக்கிறது என்பதை console செய்திகளிலிருந்து அறியலாம்.

இப்போது Effect-ன் code-ஐ custom Hook-க்குள் நகர்த்துங்கள்:

export function useChatRoom({ serverUrl, roomId }) {
useEffect(() => {
const options = {
serverUrl: serverUrl,
roomId: roomId
};
const connection = createConnection(options);
connection.connect();
connection.on('message', (msg) => {
showNotification('புதிய செய்தி: ' + msg);
});
return () => connection.disconnect();
}, [roomId, serverUrl]);
}

இதனால் உங்கள் ChatRoom component, அது உள்ளே எப்படி வேலை செய்கிறது என்பதைப் பற்றி கவலைப்படாமல் உங்கள் custom Hook-ஐ call செய்ய முடியும்:

export default function ChatRoom({ roomId }) {
const [serverUrl, setServerUrl] = useState('https://localhost:1234');

useChatRoom({
roomId: roomId,
serverUrl: serverUrl
});

return (
<>
<label>
சர்வர் URL:
<input value={serverUrl} onChange={e => setServerUrl(e.target.value)} />
</label>
<h1>{roomId} அறைக்கு வரவேற்கிறோம்!</h1>
</>
);
}

இது மிகவும் நேரடியாகத் தெரிகிறது! (ஆனால் அதே செயலைத்தான் செய்கிறது.)

logic இப்போது கூட prop மற்றும் state மாற்றங்களுக்கு பதிலளிப்பதை கவனியுங்கள். server URL அல்லது தேர்ந்தெடுத்த அறையைத் திருத்திப் பாருங்கள்:

import { useState } from 'react';
import { useChatRoom } from './useChatRoom.js';

export default function ChatRoom({ roomId }) {
  const [serverUrl, setServerUrl] = useState('https://localhost:1234');

  useChatRoom({
    roomId: roomId,
    serverUrl: serverUrl
  });

  return (
    <>
      <label>
        சர்வர் URL:
        <input value={serverUrl} onChange={e => setServerUrl(e.target.value)} />
      </label>
      <h1>{roomId} அறைக்கு வரவேற்கிறோம்!</h1>
    </>
  );
}

ஒரு Hook-ன் return value-ஐ நீங்கள் எப்படி எடுத்துக்கொள்கிறீர்கள் என்பதை கவனியுங்கள்:

export default function ChatRoom({ roomId }) {
const [serverUrl, setServerUrl] = useState('https://localhost:1234');

useChatRoom({
roomId: roomId,
serverUrl: serverUrl
});
// ...

அதை மற்றொரு Hook-க்கு input ஆக pass செய்கிறீர்கள்:

export default function ChatRoom({ roomId }) {
const [serverUrl, setServerUrl] = useState('https://localhost:1234');

useChatRoom({
roomId: roomId,
serverUrl: serverUrl
});
// ...

உங்கள் ChatRoom component ஒவ்வொரு re-render-க்கும், சமீபத்திய roomId மற்றும் serverUrl-ஐ உங்கள் Hook-க்கு pass செய்கிறது. அதனால் re-render-க்கு பிறகு அவற்றின் மதிப்புகள் வேறுபட்டிருந்தால் உங்கள் Effect chat-க்கு மீண்டும் இணைகிறது. (நீங்கள் எப்போதாவது audio அல்லது video processing software-களில் வேலை செய்திருந்தால், இவ்வாறு Hook-களை chain செய்வது visual அல்லது audio effect-களை chain செய்வதை நினைவூட்டலாம். useState-ன் output, useChatRoom-ன் input-க்கு “feed” செய்வது போல.)

event handler-களை custom Hook-களுக்கு pass செய்தல்

useChatRoom-ஐ மேலும் பல component-களில் பயன்படுத்தத் தொடங்கும்போது, அதன் நடத்தை component-கள் customize செய்ய அனுமதிக்க விரும்பலாம். உதாரணமாக, தற்போது message வந்தால் என்ன செய்ய வேண்டும் என்ற logic Hook-க்குள் hardcode செய்யப்பட்டுள்ளது:

export function useChatRoom({ serverUrl, roomId }) {
useEffect(() => {
const options = {
serverUrl: serverUrl,
roomId: roomId
};
const connection = createConnection(options);
connection.connect();
connection.on('message', (msg) => {
showNotification('புதிய செய்தி: ' + msg);
});
return () => connection.disconnect();
}, [roomId, serverUrl]);
}

இந்த logic-ஐ மீண்டும் உங்கள் component-க்குள் நகர்த்த விரும்புகிறீர்கள் என்று வைத்துக்கொள்வோம்:

export default function ChatRoom({ roomId }) {
const [serverUrl, setServerUrl] = useState('https://localhost:1234');

useChatRoom({
roomId: roomId,
serverUrl: serverUrl,
onReceiveMessage(msg) {
showNotification('புதிய செய்தி: ' + msg);
}
});
// ...

இது வேலை செய்ய, உங்கள் custom Hook அதன் named option-களில் ஒன்றாக onReceiveMessage-ஐ ஏற்கும்படி மாற்றுங்கள்:

export function useChatRoom({ serverUrl, roomId, onReceiveMessage }) {
useEffect(() => {
const options = {
serverUrl: serverUrl,
roomId: roomId
};
const connection = createConnection(options);
connection.connect();
connection.on('message', (msg) => {
onReceiveMessage(msg);
});
return () => connection.disconnect();
}, [roomId, serverUrl, onReceiveMessage]); // ✅ எல்லா dependency-களும் அறிவிக்கப்பட்டுள்ளன
}

இது வேலை செய்யும், ஆனால் உங்கள் custom Hook event handler-களை ஏற்கும்போது இன்னும் ஒரு மேம்பாட்டை செய்யலாம்.

onReceiveMessage மீது dependency சேர்ப்பது சிறந்ததல்ல, ஏனெனில் component re-render ஆகும் ஒவ்வொரு முறையும் chat மீண்டும் இணைக்கப்படும். இந்த event handler-ஐ Effect Event-க்குள் wrap செய்து dependency-களிலிருந்து அதை நீக்குங்கள்:

import { useEffect, useEffectEvent } from 'react';
// ...

export function useChatRoom({ serverUrl, roomId, onReceiveMessage }) {
const onMessage = useEffectEvent(onReceiveMessage);

useEffect(() => {
const options = {
serverUrl: serverUrl,
roomId: roomId
};
const connection = createConnection(options);
connection.connect();
connection.on('message', (msg) => {
onMessage(msg);
});
return () => connection.disconnect();
}, [roomId, serverUrl]); // ✅ எல்லா dependency-களும் அறிவிக்கப்பட்டுள்ளன
}

இப்போது ChatRoom component re-render ஆகும் ஒவ்வொரு முறையும் chat மீண்டும் இணைக்காது. custom Hook-க்கு event handler-ஐ pass செய்வதற்கான முழுமையாக வேலை செய்யும் demo இதோ; இதை நீங்கள் முயற்சி செய்யலாம்:

import { useState } from 'react';
import { useChatRoom } from './useChatRoom.js';
import { showNotification } from './notifications.js';

export default function ChatRoom({ roomId }) {
  const [serverUrl, setServerUrl] = useState('https://localhost:1234');

  useChatRoom({
    roomId: roomId,
    serverUrl: serverUrl,
    onReceiveMessage(msg) {
      showNotification('புதிய செய்தி: ' + msg);
    }
  });

  return (
    <>
      <label>
        சர்வர் URL:
        <input value={serverUrl} onChange={e => setServerUrl(e.target.value)} />
      </label>
      <h1>{roomId} அறைக்கு வரவேற்கிறோம்!</h1>
    </>
  );
}

useChatRoom-ஐ பயன்படுத்த, அது எப்படி வேலை செய்கிறது என்பதை நீங்கள் இனி அறிய வேண்டியதில்லை என்பதை கவனியுங்கள். அதை வேறு எந்த component-க்கும் சேர்த்து, வேறு option-களை pass செய்தாலும், அது அதேபோல் வேலை செய்யும். அதுதான் custom Hook-களின் பலம்.

custom Hook-களை எப்போது பயன்படுத்துவது

மீண்டும் வரும் ஒவ்வொரு சிறிய code துண்டிற்கும் custom Hook-ஐ பிரித்தெடுக்க வேண்டியதில்லை. சில duplicate code பரவாயில்லை. உதாரணமாக, முன்பு பார்த்ததுபோல் ஒரே useState call-ஐ wrap செய்ய useFormInput Hook-ஐ பிரித்தெடுப்பது பெரும்பாலும் அவசியமில்லை.

ஆனால், நீங்கள் Effect எழுதும் போதெல்லாம், அதை custom Hook-க்குள் wrap செய்தால் இன்னும் தெளிவாகுமா என்று கருதுங்கள். Effect-கள் உங்களுக்கு அடிக்கடி தேவையில்லை, எனவே ஒன்றை எழுதுகிறீர்களானால், ஏதாவது external system உடன் ஒத்திசைக்க அல்லது React-இல் உள்ளமைந்த API இல்லாத ஒன்றை செய்ய “React-க்கு வெளியே செல்ல” வேண்டியிருக்கிறது என்பதாகும். அதை custom Hook-க்குள் wrap செய்வது, உங்கள் நோக்கத்தையும் அதன் வழியாக data எப்படி பாய்கிறது என்பதையும் துல்லியமாகச் சொல்ல உதவும்.

உதாரணமாக, இரண்டு dropdown-களை காட்டும் ShippingForm component-ஐ கருதுங்கள்: ஒன்று நகரங்களின் பட்டியலைக் காட்டுகிறது, மற்றொன்று தேர்ந்தெடுத்த நகரில் உள்ள பகுதிகளின் பட்டியலைக் காட்டுகிறது. நீங்கள் இதுபோன்ற code-இல் தொடங்கலாம்:

function ShippingForm({ country }) {
const [cities, setCities] = useState(null);
// This Effect fetches cities for a country
useEffect(() => {
let ignore = false;
fetch(`/api/cities?country=${country}`)
.then(response => response.json())
.then(json => {
if (!ignore) {
setCities(json);
}
});
return () => {
ignore = true;
};
}, [country]);

const [city, setCity] = useState(null);
const [areas, setAreas] = useState(null);
// This Effect fetches areas for the selected city
useEffect(() => {
if (city) {
let ignore = false;
fetch(`/api/areas?city=${city}`)
.then(response => response.json())
.then(json => {
if (!ignore) {
setAreas(json);
}
});
return () => {
ignore = true;
};
}
}, [city]);

// ...

இந்த code மிகவும் repetitive ஆனதாக இருந்தாலும், இந்த Effect-களை ஒன்றிலிருந்து ஒன்றாகப் பிரித்தே வைத்திருப்பது சரியானது. அவை இரண்டு வேறு விஷயங்களை ஒத்திசைக்கின்றன, எனவே அவற்றை ஒரே Effect-ஆக merge செய்யக்கூடாது. அதற்கு பதிலாக, அவற்றுக்கிடையே உள்ள common logic-ஐ உங்கள் சொந்த useData Hook-க்குள் பிரித்தெடுத்து மேலுள்ள ShippingForm component-ஐ தெளிவுப்படுத்தலாம்:

function useData(url) {
const [data, setData] = useState(null);
useEffect(() => {
if (url) {
let ignore = false;
fetch(url)
.then(response => response.json())
.then(json => {
if (!ignore) {
setData(json);
}
});
return () => {
ignore = true;
};
}
}, [url]);
return data;
}

இப்போது ShippingForm component-இல் உள்ள இரண்டு Effect-களையும் useData call-களால் மாற்றலாம்:

function ShippingForm({ country }) {
const cities = useData(`/api/cities?country=${country}`);
const [city, setCity] = useState(null);
const areas = useData(city ? `/api/areas?city=${city}` : null);
// ...

custom Hook-ஐ பிரித்தெடுப்பது data flow-ஐ வெளிப்படையாக ஆக்கும். நீங்கள் url-ஐ உள்ளே கொடுக்கிறீர்கள்; data-வை வெளியே பெறுகிறீர்கள். உங்கள் Effect-ஐ useData-க்குள் “மறைப்பதன்” மூலம், ShippingForm component-இல் பணிபுரியும் ஒருவர் அதற்கு தேவையற்ற dependency-களை சேர்ப்பதையும் தடுக்கிறீர்கள். காலப்போக்கில், உங்கள் app-ன் பெரும்பாலான Effect-கள் custom Hook-களில் இருக்கும்.

Deep Dive

உங்கள் custom Hook-களை தெளிவான high-level use case-களில் கவனம் செலுத்த வைத்திருங்கள்

உங்கள் custom Hook-ன் பெயரைத் தேர்ந்தெடுப்பதிலிருந்து தொடங்குங்கள். தெளிவான பெயரைத் தேர்ந்தெடுக்க சிரமப்படுகிறீர்கள் என்றால், உங்கள் Effect உங்கள் component-ன் மீதமுள்ள logic-உடன் அதிகமாக இணைந்திருக்கிறது, இன்னும் பிரித்தெடுக்கத் தயாராக இல்லை என்பதைக் குறிக்கலாம்.

சிறந்த நிலையில், உங்கள் custom Hook-ன் பெயர், அடிக்கடி code எழுதாத ஒருவரும் கூட அது என்ன செய்கிறது, என்ன எடுக்கிறது, என்ன return செய்கிறது என்று நல்ல கணிப்பு செய்யுமளவு தெளிவாக இருக்க வேண்டும்:

  • useData(url)
  • useImpressionLog(eventName, extraData)
  • useChatRoom(options)

external system-உடன் ஒத்திசைக்கும் போது, உங்கள் custom Hook பெயர் மேலும் technical ஆகவும் அந்த system-க்கு உரிய jargon-ஐ பயன்படுத்துவதாகவும் இருக்கலாம். அந்த system-ஐ அறிந்த ஒருவருக்கு அது தெளிவாக இருந்தால் அது சரிதான்:

  • useMediaQuery(query)
  • useSocket(url)
  • useIntersectionObserver(ref, options)

custom Hook-களை தெளிவான high-level use case-களில் கவனம் செலுத்த வைத்திருங்கள். useEffect API-க்கே மாற்றாகவும் வசதியான wrapper-களாகவும் நடக்கும் custom “lifecycle” Hook-களை உருவாக்கி பயன்படுத்துவதைத் தவிருங்கள்:

  • 🔴 useMount(fn)
  • 🔴 useEffectOnce(fn)
  • 🔴 useUpdateEffect(fn)

உதாரணமாக, இந்த useMount Hook சில code “mount ஆகும் போது” மட்டுமே இயங்குவதை உறுதிசெய்ய முயற்சிக்கிறது:

function ChatRoom({ roomId }) {
const [serverUrl, setServerUrl] = useState('https://localhost:1234');

// 🔴 Avoid: using custom "lifecycle" Hooks
useMount(() => {
const connection = createConnection({ roomId, serverUrl });
connection.connect();

post('/analytics/event', { eventName: 'visit_chat' });
});
// ...
}

// 🔴 Avoid: creating custom "lifecycle" Hooks
function useMount(fn) {
useEffect(() => {
fn();
}, []); // 🔴 React Hook useEffect-க்கு dependency 'fn' இல்லை
}

useMount போன்ற custom “lifecycle” Hook-கள் React paradigm-க்கு நன்றாக பொருந்தாது. உதாரணமாக, இந்த code உதாரணத்தில் ஒரு தவறு உள்ளது (roomId அல்லது serverUrl மாற்றங்களுக்கு இது “react” செய்யவில்லை), ஆனால் linter இதைப் பற்றி எச்சரிக்காது, ஏனெனில் linter நேரடி useEffect call-களை மட்டுமே சரிபார்க்கிறது. உங்கள் Hook பற்றி அதற்கு தெரியாது.

நீங்கள் Effect எழுதுகிறீர்களானால், React API-யை நேரடியாகப் பயன்படுத்துவதிலிருந்து தொடங்குங்கள்:

function ChatRoom({ roomId }) {
const [serverUrl, setServerUrl] = useState('https://localhost:1234');

// ✅ Good: two raw Effects separated by purpose

useEffect(() => {
const connection = createConnection({ serverUrl, roomId });
connection.connect();
return () => connection.disconnect();
}, [serverUrl, roomId]);

useEffect(() => {
post('/analytics/event', { eventName: 'visit_chat', roomId });
}, [roomId]);

// ...
}

பிறகு, வேறு high-level use case-களுக்காக custom Hook-களை பிரித்தெடுக்கலாம் (ஆனால் அவசியமில்லை):

function ChatRoom({ roomId }) {
const [serverUrl, setServerUrl] = useState('https://localhost:1234');

// ✅ Great: custom Hooks named after their purpose
useChatRoom({ serverUrl, roomId });
useImpressionLog('visit_chat', { roomId });
// ...
}

ஒரு நல்ல custom Hook அது செய்யக்கூடியவற்றைக் கட்டுப்படுத்துவதன் மூலம் calling code-ஐ மேலும் declarative ஆக்குகிறது. உதாரணமாக, useChatRoom(options) chat room-க்கு மட்டுமே இணைக்க முடியும்; useImpressionLog(eventName, extraData) analytics-க்கு impression log-ஐ மட்டுமே அனுப்ப முடியும். உங்கள் custom Hook API use case-களை கட்டுப்படுத்தாமல் மிகவும் abstract ஆக இருந்தால், நீண்ட காலத்தில் அது தீர்ப்பதைவிட அதிக பிரச்சினைகளை உருவாக்க வாய்ப்புள்ளது.

Custom Hook-கள் மேம்பட்ட pattern-களுக்கு migrate செய்ய உதவும்

Effect-கள் ஒரு “escape hatch”: நீங்கள் “React-க்கு வெளியே செல்ல” வேண்டியபோது, மேலும் உங்கள் use case-க்காக சிறந்த உள்ளமைந்த தீர்வு இல்லாதபோது அவற்றைப் பயன்படுத்துகிறீர்கள். காலப்போக்கில், குறிப்பிட்ட பிரச்சினைகளுக்கு மேலும் குறிப்பிட்ட தீர்வுகளை வழங்குவதன் மூலம் உங்கள் app-இல் உள்ள Effect-களின் எண்ணிக்கையை குறைந்தபட்சமாகக் குறைப்பதே React குழுவின் இலக்கு. உங்கள் Effect-களை custom Hook-களில் wrap செய்வது, இத்தகைய தீர்வுகள் கிடைக்கும்போது உங்கள் code-ஐ upgrade செய்வதை உதவுகிறது.

இந்த உதாரணத்திற்குத் திரும்புவோம்:

import { useState, useEffect } from 'react';

export function useOnlineStatus() {
  const [isOnline, setIsOnline] = useState(true);
  useEffect(() => {
    function handleOnline() {
      setIsOnline(true);
    }
    function handleOffline() {
      setIsOnline(false);
    }
    window.addEventListener('online', handleOnline);
    window.addEventListener('offline', handleOffline);
    return () => {
      window.removeEventListener('online', handleOnline);
      window.removeEventListener('offline', handleOffline);
    };
  }, []);
  return isOnline;
}

மேலுள்ள உதாரணத்தில், useOnlineStatus useState மற்றும் useEffect. ஜோடியால் implement செய்யப்பட்டுள்ளது. ஆனால் இது சாத்தியமான சிறந்த தீர்வு அல்ல. இது கருதாத பல edge case-கள் உள்ளன. உதாரணமாக, component mount ஆகும் போது isOnline ஏற்கனவே true என்று இது கருதுகிறது; ஆனால் network ஏற்கனவே offline சென்றிருந்தால் இது தவறாக இருக்கலாம். இதைச் சரிபார்க்க browser navigator.onLine API-யைப் பயன்படுத்தலாம், ஆனால் அதை நேரடியாகப் பயன்படுத்துவது server-இல் initial HTML உருவாக்கும்போது வேலை செய்யாது. சுருக்கமாக, இந்த code மேம்படுத்தப்படலாம்.

React-இல் useSyncExternalStore என்ற dedicated API உள்ளது; இது இந்த எல்லா பிரச்சினைகளையும் உங்களுக்காக கவனிக்கிறது. இந்த புதிய API-யைப் பயன்படுத்துமாறு மீண்டும் எழுதப்பட்ட உங்கள் useOnlineStatus Hook இதோ:

import { useSyncExternalStore } from 'react';

function subscribe(callback) {
  window.addEventListener('online', callback);
  window.addEventListener('offline', callback);
  return () => {
    window.removeEventListener('online', callback);
    window.removeEventListener('offline', callback);
  };
}

export function useOnlineStatus() {
  return useSyncExternalStore(
    subscribe,
    () => navigator.onLine, // client-இல் மதிப்பை பெறுவது எப்படி
    () => true // server-இல் மதிப்பை பெறுவது எப்படி
  );
}

இந்த migration-ஐ செய்ய எந்த component-ஐயும் மாற்ற வேண்டியிருக்கவில்லை என்பதை கவனியுங்கள்:

function StatusBar() {
const isOnline = useOnlineStatus();
// ...
}

function SaveButton() {
const isOnline = useOnlineStatus();
// ...
}

Effect-களை custom Hook-களில் wrap செய்வது ஏன் பெரும்பாலும் பயனுள்ளதாக இருக்கிறது என்பதற்கான இன்னொரு காரணம் இதுதான்:

  1. உங்கள் Effect-களுக்குள் செல்லும் மற்றும் வெளியே வரும் data flow-ஐ மிகவும் வெளிப்படையாக ஆக்குகிறீர்கள்.
  2. உங்கள் Effect-களின் சரியான implementation-ஐ விட, உங்கள் component-கள் நோக்கத்தில் கவனம் செலுத்த அனுமதிக்கிறீர்கள்.
  3. React புதிய அம்சங்களைச் சேர்க்கும்போது, எந்த component-ஐயும் மாற்றாமல் அந்த Effect-களை நீக்க முடியும்.

design system போல, உங்கள் app-ன் component-களிலிருந்து common idiom-களை custom Hook-களாக பிரித்தெடுக்கத் தொடங்குவது பயனுள்ளதாக இருக்கலாம். இது உங்கள் component-களின் code-ஐ நோக்கத்தில் கவனம் செலுத்த வைத்திருக்கும்; மேலும் raw Effect-களை அடிக்கடி எழுதுவதைத் தவிர்க்க உதவும். பல சிறந்த custom Hook-களை React community பராமரிக்கிறது.

Deep Dive

data fetching-க்காக React உள்ளமைந்த தீர்வை வழங்குமா?

இன்று, use API மூலம், Promise-ஐ use-க்கு pass செய்து render-இல் data-வைப் படிக்கலாம்:

import { use, Suspense } from "react";

function Message({ messagePromise }) {
const messageContent = use(messagePromise);
return <p>செய்தி இதோ: {messageContent}</p>;
}

export function MessageContainer({ messagePromise }) {
return (
<Suspense fallback={<p>⌛செய்தி பதிவிறங்குகிறது...</p>}>
<Message messagePromise={messagePromise} />
</Suspense>
);
}

நாங்கள் இன்னும் விவரங்களில் பணிபுரிந்து கொண்டிருக்கிறோம், ஆனால் எதிர்காலத்தில் நீங்கள் data fetching-ஐ இதுபோல் எழுதுவீர்கள் என்று எதிர்பார்க்கிறோம்:

import { use } from 'react';

function ShippingForm({ country }) {
const cities = use(fetch(`/api/cities?country=${country}`));
const [city, setCity] = useState(null);
const areas = city ? use(fetch(`/api/areas?city=${city}`)) : null;
// ...

உங்கள் app-இல் மேலே உள்ள useData போன்ற custom Hook-களைப் பயன்படுத்தினால், ஒவ்வொரு component-இலும் raw Effect-களை கைமுறையாக எழுதுவதைக் காட்டிலும், இறுதியில் பரிந்துரைக்கப்படும் அணுகுமுறைக்கு migrate செய்ய குறைவான மாற்றங்கள் போதும். ஆனால் பழைய அணுகுமுறையும் தொடர்ந்து நன்றாகவே வேலை செய்யும், எனவே raw Effect-களை எழுதுவதில் நீங்கள் வசதியாக இருந்தால் அதைத் தொடரலாம்.

இதைச் செய்வதற்கு ஒன்றுக்கு மேற்பட்ட வழிகள் உள்ளன

browser requestAnimationFrame API-யைப் பயன்படுத்தி fade-in animation-ஐ ஆரம்பத்திலிருந்து implement செய்ய விரும்புகிறீர்கள் என்று வைத்துக்கொள்வோம். animation loop-ஐ அமைக்கும் Effect-இல் நீங்கள் தொடங்கலாம். animation-ன் ஒவ்வொரு frame-இலும், 1-ஐ அடையும் வரை நீங்கள் ref-இல் வைத்திருக்கும் DOM node-ன் opacity-ஐ மாற்றலாம். உங்கள் code இதுபோல் தொடங்கலாம்:

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

function Welcome() {
  const ref = useRef(null);

  useEffect(() => {
    const duration = 1000;
    const node = ref.current;

    let startTime = performance.now();
    let frameId = null;

    function onFrame(now) {
      const timePassed = now - startTime;
      const progress = Math.min(timePassed / duration, 1);
      onProgress(progress);
      if (progress < 1) {
        // We still have more frames to paint
        frameId = requestAnimationFrame(onFrame);
      }
    }

    function onProgress(progress) {
      node.style.opacity = progress;
    }

    function start() {
      onProgress(0);
      startTime = performance.now();
      frameId = requestAnimationFrame(onFrame);
    }

    function stop() {
      cancelAnimationFrame(frameId);
      startTime = null;
      frameId = null;
    }

    start();
    return () => stop();
  }, []);

  return (
    <h1 className="welcome" ref={ref}>
      வரவேற்கிறோம்
    </h1>
  );
}

export default function App() {
  const [show, setShow] = useState(false);
  return (
    <>
      <button onClick={() => setShow(!show)}>
        {show ? 'அகற்று' : 'காட்டு'}
      </button>
      <hr />
      {show && <Welcome />}
    </>
  );
}

component-ஐ வாசிக்க மேம்படுத்த, logic-ஐ useFadeIn custom Hook-க்குள் பிரித்தெடுக்கலாம்:

import { useState, useEffect, useRef } from 'react';
import { useFadeIn } from './useFadeIn.js';

function Welcome() {
  const ref = useRef(null);

  useFadeIn(ref, 1000);

  return (
    <h1 className="welcome" ref={ref}>
      வரவேற்கிறோம்
    </h1>
  );
}

export default function App() {
  const [show, setShow] = useState(false);
  return (
    <>
      <button onClick={() => setShow(!show)}>
        {show ? 'அகற்று' : 'காட்டு'}
      </button>
      <hr />
      {show && <Welcome />}
    </>
  );
}

useFadeIn code-ஐ அப்படியே வைத்திருக்கலாம், ஆனால் அதை மேலும் refactor செய்யவும் முடியும். உதாரணமாக, animation loop-ஐ அமைக்கும் logic-ஐ useFadeIn-இலிருந்து custom useAnimationLoop Hook-க்குள் பிரித்தெடுக்கலாம்:

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

export function useFadeIn(ref, duration) {
  const [isRunning, setIsRunning] = useState(true);

  useAnimationLoop(isRunning, (timePassed) => {
    const progress = Math.min(timePassed / duration, 1);
    ref.current.style.opacity = progress;
    if (progress === 1) {
      setIsRunning(false);
    }
  });
}

function useAnimationLoop(isRunning, drawFrame) {
  const onFrame = useEffectEvent(drawFrame);

  useEffect(() => {
    if (!isRunning) {
      return;
    }

    const startTime = performance.now();
    let frameId = null;

    function tick(now) {
      const timePassed = now - startTime;
      onFrame(timePassed);
      frameId = requestAnimationFrame(tick);
    }

    tick();
    return () => cancelAnimationFrame(frameId);
  }, [isRunning]);
}

ஆனால், அதை நீங்கள் கட்டாயமாக செய்ய வேண்டியதில்லை. சாதாரண function-களைப் போலவே, உங்கள் code-ன் வேறு பகுதிகளுக்கிடையே எல்லைகளை எங்கு வரைய வேண்டும் என்பதை இறுதியில் நீங்களே தீர்மானிக்கிறீர்கள். மிகவும் வேறுபட்ட அணுகுமுறையையும் எடுக்கலாம். Effect-இல் logic-ஐ வைத்திருப்பதற்கு பதிலாக, பெரும்பாலான imperative logic-ஐ JavaScript class-க்குள் நகர்த்தலாம்:

import { useState, useEffect } from 'react';
import { FadeInAnimation } from './animation.js';

export function useFadeIn(ref, duration) {
  useEffect(() => {
    const animation = new FadeInAnimation(ref.current);
    animation.start(duration);
    return () => {
      animation.stop();
    };
  }, [ref, duration]);
}

Effect-கள் React-ஐ external system-களுடன் இணைக்க உதவுகின்றன. Effect-களுக்கிடையே அதிக coordination தேவைப்படும் போது (உதாரணமாக, பல animation-களை chain செய்வதற்கு), அந்த logic-ஐ மேலுள்ள sandbox போல Effect-களிலும் Hook-களிலும் இருந்து முழுமையாக வெளியே பிரித்தெடுப்பது அதிக பொருள் கொள்ளும். பிறகு, நீங்கள் பிரித்தெடுத்த code தானே “external system” ஆகிறது. இதனால் உங்கள் Effect-கள் நேரடியாக இருக்கும், ஏனெனில் அவை React-க்கு வெளியே நகர்த்திய system-க்கு message-களை அனுப்புவதுமே அவற்றின் பணி.

மேலுள்ள உதாரணங்கள் fade-in logic JavaScript-இல் எழுதப்பட வேண்டும் என்று கருதுகின்றன. ஆனால் இந்த குறிப்பிட்ட fade-in animation-ஐ சாதாரண CSS Animation மூலம் implement செய்வது இன்னும் தெளிவுயும் மிகவும் efficient-ஆகவும் இருக்கும்:

.welcome {
  color: white;
  padding: 50px;
  text-align: center;
  font-size: 50px;
  background-image: radial-gradient(circle, rgba(63,94,251,1) 0%, rgba(252,70,107,1) 100%);

  animation: fadeIn 1000ms;
}

@keyframes fadeIn {
  0% { opacity: 0; }
  100% { opacity: 1; }
}

சில நேரங்களில், Hook கூட தேவையில்லை!

Recap

  • Custom Hook-கள் component-களுக்கிடையே logic-ஐப் பகிர உதவும்.
  • Custom Hook-களின் பெயர் use-க்கு பின் capital letter வரும் வகையில் தொடங்க வேண்டும்.
  • Custom Hook-கள் stateful logic-ஐ மட்டுமே பகிரும்; state-ஐயே அல்ல.
  • ஒரு Hook-இலிருந்து மற்றொரு Hook-க்கு reactive மதிப்புகளை pass செய்யலாம்; அவை up-to-date ஆக இருக்கும்.
  • உங்கள் component re-render ஆகும் ஒவ்வொரு முறையும் எல்லா Hook-களும் மீண்டும் இயங்கும்.
  • உங்கள் custom Hook-களின் code, உங்கள் component code போல pure ஆக இருக்க வேண்டும்.
  • custom Hook-கள் பெறும் event handler-களை Effect Event-களுக்குள் wrap செய்யுங்கள்.
  • useMount போன்ற custom Hook-களை உருவாக்க வேண்டாம். அவற்றின் நோக்கத்தை குறிப்பிட்டதாக வைத்திருங்கள்.
  • உங்கள் code-ன் எல்லைகளை எப்படி, எங்கே தேர்வு செய்வது என்பது உங்கள் முடிவு.

Challenge 1 of 5:
useCounter Hook-ஐப் பிரித்தெடுக்கவும்

இந்த component ஒவ்வொரு விநாடியும் அதிகரிக்கும் எண்ணைக் காட்ட state variable மற்றும் Effect-ஐப் பயன்படுத்துகிறது. இந்த logic-ஐ useCounter என்ற custom Hook-க்குள் பிரித்தெடுக்கவும். உங்கள் இலக்கு Counter component implementation இதுபோலவே தோன்றச் செய்வது:

export default function Counter() {
const count = useCounter();
return <h1>கடந்த விநாடிகள்: {count}</h1>;
}

உங்கள் custom Hook-ஐ useCounter.js-இல் எழுதிப், அதை App.js file-க்குள் import செய்ய வேண்டும்.

import { useState, useEffect } from 'react';

export default function Counter() {
  const [count, setCount] = useState(0);
  useEffect(() => {
    const id = setInterval(() => {
      setCount(c => c + 1);
    }, 1000);
    return () => clearInterval(id);
  }, []);
  return <h1>கடந்த விநாடிகள்: {count}</h1>;
}