useEffect
useEffect என்பது ஒரு component-ஐ external system உடன் synchronize செய்ய அனுமதிக்கும் React Hook.
useEffect(setup, dependencies?)- Reference
- Usage
- External system-க்கு connect செய்தல்
- Effects-ஐ custom Hooks-இல் wrap செய்தல்
- Non-React widget-ஐ control செய்தல்
- Effects மூலம் data fetch செய்தல்
- Reactive dependencies specify செய்தல்
- Effect-இலிருந்து previous state அடிப்படையில் state update செய்தல்
- தேவையற்ற object dependencies-ஐ நீக்குதல்
- தேவையற்ற function dependencies-ஐ நீக்குதல்
- Effect-இலிருந்து latest props மற்றும் state படித்தல்
- Server மற்றும் client-இல் வேறுபட்ட content display செய்தல்
- Troubleshooting
Reference
useEffect(setup, dependencies?)
Effect ஒன்றை declare செய்ய உங்கள் component-ன் top level-இல் useEffect call செய்யுங்கள்:
import { useState, useEffect } from 'react';
import { createConnection } from './chat.js';
function ChatRoom({ roomId }) {
const [serverUrl, setServerUrl] = useState('https://localhost:1234');
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.connect();
return () => {
connection.disconnect();
};
}, [serverUrl, roomId]);
// ...
}மேலும் examples கீழே பார்க்கவும்.
Parameters
-
setup: உங்கள் Effect-ன் logic கொண்ட function. உங்கள் setup function optional ஆக ஒரு cleanup function-ஐ return செய்யலாம். உங்கள் component commit ஆனதும், React உங்கள் setup function-ஐ run செய்யும். Dependencies மாறிய ஒவ்வொரு commit-க்கும் பிறகு, React முதலில் பழைய values உடன் cleanup function-ஐ (நீங்கள் கொடுத்திருந்தால்) run செய்து, பிறகு புதிய values உடன் setup function-ஐ run செய்யும். உங்கள் component DOM-இலிருந்து remove செய்யப்பட்ட பிறகு, React உங்கள் cleanup function-ஐ run செய்யும். -
optional
dependencies:setupcode-க்குள் referenced ஆன எல்லா reactive values-ன் list. Reactive values-இல் props, state, மற்றும் உங்கள் component body-க்குள் நேரடியாக declared செய்யப்பட்ட எல்லா variables மற்றும் functions அடங்கும். உங்கள் linter React-க்காக configured செய்யப்பட்டிருந்தால், ஒவ்வொரு reactive value-உம் dependency ஆக சரியாக specified உள்ளதா என்று verify செய்யும். Dependencies list-இல் constant number of items இருக்க வேண்டும்; மேலும்[dep1, dep2, dep3]போல inline ஆக எழுதப்பட வேண்டும். React ஒவ்வொரு dependency-யையும் அதன் previous value உடன்Object.iscomparison பயன்படுத்தி compare செய்யும். இந்த argument-ஐ omit செய்தால், component-ன் ஒவ்வொரு commit-க்கும் பிறகு உங்கள் Effect re-run ஆகும். Dependencies array, empty array, மற்றும் dependency ஏதும் இல்லாமல் pass செய்வதின் வேறுபாட்டைப் பார்க்கவும்.
Returns
useEffect undefined return செய்கிறது.
Caveats
-
useEffectஒரு Hook என்பதால், அதை உங்கள் component-ன் top level அல்லது உங்கள் சொந்த Hooks-இல் மட்டுமே call செய்யலாம். Loops அல்லது conditions-க்குள் call செய்ய முடியாது. அது தேவைப்பட்டால், புதிய component ஒன்றை extract செய்து state-ஐ அதற்குள் move செய்யுங்கள். -
நீங்கள் ஏதாவது external system உடன் synchronize செய்ய முயற்சிக்கவில்லை என்றால், உங்களுக்கு Effect தேவைப்படாமல் இருக்கலாம்.
-
Strict Mode on ஆக இருந்தால், முதல் real setup-க்கு முன் React development-only ஆன கூடுதல் setup+cleanup cycle ஒன்றை run செய்யும். உங்கள் cleanup logic, setup logic-ஐ “mirror” செய்கிறதா மற்றும் setup செய்கிறதை stop அல்லது undo செய்கிறதா என்பதை உறுதி செய்யும் stress-test இது. இது பிரச்சினை ஏற்படுத்தினால், cleanup function-ஐ implement செய்யுங்கள்.
-
உங்கள் dependencies-இல் சில objects அல்லது component-க்குள் defined ஆன functions இருந்தால், அவை தேவையானதை விட அதிகமாக Effect re-run ஆக காரணமாகும் அபாயம் உள்ளது. இதை fix செய்ய, தேவையற்ற object மற்றும் function dependencies-ஐ remove செய்யுங்கள். உங்கள் Effect-க்கு வெளியே state updates மற்றும் non-reactive logic-ஐயும் extract செய்யலாம்.
-
உங்கள் Effect interaction (click போன்றது) காரணமாக ஏற்பட்டதல்ல என்றால், React பொதுவாக browser-க்கு உங்கள் Effect run செய்வதற்கு முன் updated screen-ஐ முதலில் paint செய்ய அனுமதிக்கும். உங்கள் Effect visual ஏதாவது செய்கிறதானால் (உதாரணமாக tooltip position செய்தல்), delay கவனிக்கத்தக்கதாக இருந்தால் (உதாரணமாக flicker ஆனால்),
useEffect-ஐuseLayoutEffect-ஆக மாற்றுங்கள். -
உங்கள் Effect interaction (click போன்றது) காரணமாக ஏற்பட்டிருந்தால், browser updated screen-ஐ paint செய்வதற்கு முன் React உங்கள் Effect-ஐ run செய்யலாம். Effect-ன் result-ஐ event system observe செய்ய முடியும் என்பதை இது உறுதி செய்கிறது. பொதுவாக இது எதிர்பார்த்தபடி வேலை செய்கிறது. ஆனால்
alert()போன்ற work-ஐ paint-க்கு பிறகு வரை defer செய்ய வேண்டுமெனில்,setTimeoutuse செய்யலாம். மேலும் தகவலுக்கு reactwg/react-18/128 பார்க்கவும். -
உங்கள் Effect interaction (click போன்றது) காரணமாக இருந்தாலும், உங்கள் Effect-க்குள் உள்ள state updates-ஐ process செய்வதற்கு முன் browser screen-ஐ repaint செய்ய React அனுமதிக்கலாம். பொதுவாக இது எதிர்பார்த்தபடி வேலை செய்கிறது. ஆனால் browser screen-ஐ repaint செய்வதை block செய்ய வேண்டுமெனில்,
useEffect-ஐuseLayoutEffect-ஆக மாற்ற வேண்டும். -
Effects client-இல் மட்டுமே run ஆகும். Server rendering போது அவை run ஆகாது.
Usage
External system-க்கு connect செய்தல்
சில components page-இல் display ஆகும் போது network, browser API, அல்லது third-party library ஒன்றுடன் connected ஆகவே இருக்க வேண்டும். இந்த systems React மூலம் controlled அல்ல; எனவே அவை external என்று அழைக்கப்படுகின்றன.
உங்கள் component-ஐ ஏதாவது external system-க்கு connect செய்ய, உங்கள் component-ன் top level-இல் useEffect call செய்யுங்கள்:
import { useState, useEffect } from 'react';
import { createConnection } from './chat.js';
function ChatRoom({ roomId }) {
const [serverUrl, setServerUrl] = useState('https://localhost:1234');
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.connect();
return () => {
connection.disconnect();
};
}, [serverUrl, roomId]);
// ...
}useEffect-க்கு இரண்டு arguments pass செய்ய வேண்டும்:
- அந்த system-க்கு connect செய்யும் setup code கொண்ட setup function.
- அந்த system-இலிருந்து disconnect செய்யும் cleanup code கொண்ட cleanup function-ஐ அது return செய்ய வேண்டும்.
- அந்த functions-க்குள் use செய்யப்பட்ட உங்கள் component-இலிருந்து வரும் ஒவ்வொரு value-யையும் உள்ளடக்கும் dependencies list.
தேவைப்படும் ஒவ்வொரு முறையும் React உங்கள் setup மற்றும் cleanup functions-ஐ call செய்யும்; இது பல முறை நடக்கலாம்:
- உங்கள் component page-க்கு add செய்யப்படும் போது (mounts) உங்கள் setup code run ஆகும்.
- dependencies மாறிய உங்கள் component-ன் ஒவ்வொரு commit-க்கும் பிறகு:
- முதலில், பழைய props மற்றும் state உடன் உங்கள் cleanup code run ஆகும்.
- பின்னர், புதிய props மற்றும் state உடன் உங்கள் setup code run ஆகும்.
- உங்கள் component page-இலிருந்து remove செய்யப்பட்ட பிறகு (unmounts) உங்கள் cleanup code கடைசியாக ஒருமுறை run ஆகும்.
மேலுள்ள example-க்கு இந்த sequence-ஐ விளக்கிப் பார்ப்போம்.
மேலுள்ள ChatRoom component page-க்கு add செய்யப்படும் போது, அது initial serverUrl மற்றும் roomId உடன் chat room-க்கு connect ஆகும். Commit காரணமாக serverUrl அல்லது roomId ஏதாவது மாறினால் (உதாரணமாக user dropdown-இல் வேறு chat room தேர்வு செய்தால்), உங்கள் Effect முந்தைய room-இலிருந்து disconnect செய்து, அடுத்த room-க்கு connect ஆகும். ChatRoom component page-இலிருந்து remove செய்யப்படும் போது, உங்கள் Effect கடைசியாக ஒருமுறை disconnect செய்யும்.
Bugs கண்டுபிடிக்க உதவ, development-இல் React setup-க்கு முன் setup மற்றும் cleanup-ஐ கூடுதலாக ஒருமுறை run செய்கிறது. உங்கள் Effect-ன் logic சரியாக implemented உள்ளதா என்பதை verify செய்யும் stress-test இது. இது visible issues ஏற்படுத்தினால், உங்கள் cleanup function-இல் சில logic missing. Cleanup function setup function செய்ததை stop அல்லது undo செய்ய வேண்டும். Rule of thumb: setup ஒருமுறை call செய்யப்படுவது (production போல) மற்றும் setup → cleanup → setup sequence (development போல) இடையே user வேறுபாடு காணக்கூடாது. பொதுவான solutions பார்க்கவும்.
ஒவ்வொரு Effect-ஐயும் independent process ஆக எழுத முயற்சிக்கவும்; மேலும் ஒரு setup/cleanup cycle பற்றி மட்டும் ஒரே நேரத்தில் சிந்திக்கவும். உங்கள் component mounting, updating, அல்லது unmounting ஆகியவற்றில் எது நடக்கிறது என்பது முக்கியமாக இருக்கக்கூடாது. Cleanup logic setup logic-ஐ சரியாக “mirror” செய்தால், setup மற்றும் cleanup தேவையான அளவு அடிக்கடி run ஆனாலும் உங்கள் Effect resilient ஆக இருக்கும்.
Example 1 of 5: Chat server-க்கு connect செய்தல்
இந்த example-இல், ChatRoom component chat.js-இல் defined செய்யப்பட்ட external system உடன் connected ஆக இருக்க Effect use செய்கிறது. ChatRoom component தோன்ற “Chat-ஐ திற” அழுத்துங்கள். இந்த sandbox development mode-இல் run ஆகிறது; எனவே இங்கே விளக்கப்பட்டுள்ளபடி கூடுதல் connect-and-disconnect cycle ஒன்று உள்ளது. Dropdown மற்றும் input பயன்படுத்தி roomId மற்றும் serverUrl மாற்றிப் பார்த்து, Effect chat-க்கு எப்படி re-connect ஆகிறது என்பதை பாருங்கள். Effect கடைசியாக ஒருமுறை disconnect ஆகுவதைக் காண “Chat-ஐ மூடு” அழுத்துங்கள்.
import { useState, useEffect } from 'react'; import { createConnection } from './chat.js'; function ChatRoom({ roomId }) { const [serverUrl, setServerUrl] = useState('https://localhost:1234'); useEffect(() => { const connection = createConnection(serverUrl, roomId); connection.connect(); return () => { connection.disconnect(); }; }, [roomId, serverUrl]); return ( <> <label> Server URL:{' '} <input value={serverUrl} onChange={e => setServerUrl(e.target.value)} /> </label> <h1>{roomId} அறைக்கு வரவேற்கிறோம்!</h1> </> ); } export default function App() { const [roomId, setRoomId] = useState('general'); const [show, setShow] = useState(false); return ( <> <label> Chat room-ஐ தேர்வு செய்க:{' '} <select value={roomId} onChange={e => setRoomId(e.target.value)} > <option value="general">பொது</option> <option value="travel">பயணம்</option> <option value="music">இசை</option> </select> </label> <button onClick={() => setShow(!show)}> {show ? 'Chat-ஐ மூடு' : 'Chat-ஐ திற'} </button> {show && <hr />} {show && <ChatRoom roomId={roomId} />} </> ); }
Effects-ஐ custom Hooks-இல் wrap செய்தல்
Effects ஒரு “escape hatch”: நீங்கள் “React-க்கு வெளியே step செய்ய” வேண்டியபோது மற்றும் உங்கள் use case-க்கு சிறந்த built-in solution இல்லாதபோது அவற்றை use செய்கிறீர்கள். Effects-ஐ அடிக்கடி manual ஆக எழுத வேண்டிய நிலை உங்களுக்கு வந்தால், உங்கள் components சார்ந்திருக்கும் common behaviors-க்கு சில custom Hooks extract செய்ய வேண்டும் என்பதற்கான அறிகுறியாக அது இருக்கும்.
உதாரணமாக, இந்த useChatRoom custom Hook உங்கள் Effect-ன் logic-ஐ இன்னும் declarative API பின்னால் “hide” செய்கிறது:
function useChatRoom({ serverUrl, roomId }) {
useEffect(() => {
const options = {
serverUrl: serverUrl,
roomId: roomId
};
const connection = createConnection(options);
connection.connect();
return () => connection.disconnect();
}, [roomId, serverUrl]);
}பிறகு எந்த component-இலிருந்தும் அதை இவ்வாறு use செய்யலாம்:
function ChatRoom({ roomId }) {
const [serverUrl, setServerUrl] = useState('https://localhost:1234');
useChatRoom({
roomId: roomId,
serverUrl: serverUrl
});
// ...React ecosystem-இல் ஒவ்வொரு purpose-க்கும் பல சிறந்த custom Hooks கிடைக்கின்றன.
Example 1 of 3: Custom useChatRoom Hook
இந்த example முந்தைய examples-இல் ஒன்றுக்கு identical; ஆனால் logic custom Hook-க்கு extract செய்யப்பட்டுள்ளது.
import { useState } from 'react'; import { useChatRoom } from './useChatRoom.js'; function ChatRoom({ roomId }) { const [serverUrl, setServerUrl] = useState('https://localhost:1234'); useChatRoom({ roomId: roomId, serverUrl: serverUrl }); return ( <> <label> Server URL:{' '} <input value={serverUrl} onChange={e => setServerUrl(e.target.value)} /> </label> <h1>{roomId} அறைக்கு வரவேற்கிறோம்!</h1> </> ); } export default function App() { const [roomId, setRoomId] = useState('general'); const [show, setShow] = useState(false); return ( <> <label> Chat room-ஐ தேர்வு செய்க:{' '} <select value={roomId} onChange={e => setRoomId(e.target.value)} > <option value="general">பொது</option> <option value="travel">பயணம்</option> <option value="music">இசை</option> </select> </label> <button onClick={() => setShow(!show)}> {show ? 'Chat-ஐ மூடு' : 'Chat-ஐ திற'} </button> {show && <hr />} {show && <ChatRoom roomId={roomId} />} </> ); }
Non-React widget-ஐ control செய்தல்
சில நேரங்களில், external system ஒன்றை உங்கள் component-ன் prop அல்லது state ஒன்றுடன் synchronized ஆக வைத்திருக்க விரும்புவீர்கள்.
உதாரணமாக, React இல்லாமல் எழுதப்பட்ட third-party map widget அல்லது video player component உங்களிடம் இருந்தால், அதன் state உங்கள் React component-ன் current state-க்கு match ஆகும் வகையில் அதில் methods call செய்ய Effect use செய்யலாம். இந்த Effect map-widget.js-இல் defined செய்யப்பட்ட MapWidget class-ன் instance ஒன்றை create செய்கிறது. Map component-ன் zoomLevel prop-ஐ மாற்றும்போது, அதை synchronized ஆக வைத்திருக்க Effect class instance-இல் setZoom() call செய்கிறது:
import { useRef, useEffect } from 'react'; import { MapWidget } from './map-widget.js'; export default function Map({ zoomLevel }) { const containerRef = useRef(null); const mapRef = useRef(null); useEffect(() => { if (mapRef.current === null) { mapRef.current = new MapWidget(containerRef.current); } const map = mapRef.current; map.setZoom(zoomLevel); }, [zoomLevel]); return ( <div style={{ width: 200, height: 200 }} ref={containerRef} /> ); }
இந்த example-இல் cleanup function தேவையில்லை; ஏனெனில் MapWidget class அதற்கு pass செய்யப்பட்ட DOM node-ஐ மட்டும் manage செய்கிறது. Map React component tree-இலிருந்து remove செய்யப்பட்ட பிறகு, DOM node மற்றும் MapWidget class instance இரண்டும் browser JavaScript engine மூலம் automatic ஆக garbage-collected ஆகும்.
Effects மூலம் data fetch செய்தல்
உங்கள் component-க்காக data fetch செய்ய Effect use செய்யலாம். நீங்கள் framework use செய்தால், Effects-ஐ manual ஆக எழுதுவதைக் காட்டிலும் உங்கள் framework-ன் data fetching mechanism use செய்வது மிகவும் efficient ஆக இருக்கும் என்பதை கவனிக்கவும்.
Effect-இலிருந்து data-ஐ manual ஆக fetch செய்ய விரும்பினால், உங்கள் code இவ்வாறு இருக்கலாம்:
import { useState, useEffect } from 'react';
import { fetchBio } from './api.js';
export default function Page() {
const [person, setPerson] = useState('Alice');
const [bio, setBio] = useState(null);
useEffect(() => {
let ignore = false;
setBio(null);
fetchBio(person).then(result => {
if (!ignore) {
setBio(result);
}
});
return () => {
ignore = true;
};
}, [person]);
// ...false ஆக initialized செய்யப்பட்டு cleanup போது true ஆக set செய்யப்படும் ignore variable-ஐ கவனியுங்கள். Network responses நீங்கள் அனுப்பிய order-இல் அல்லாமல் வேறு order-இல் வரக்கூடும்; உங்கள் code “race conditions” காரணமாக பாதிக்கப்படாமல் இருப்பதை இது உறுதி செய்கிறது.
import { useState, useEffect } from 'react'; import { fetchBio } from './api.js'; export default function Page() { const [person, setPerson] = useState('Alice'); const [bio, setBio] = useState(null); useEffect(() => { let ignore = false; setBio(null); fetchBio(person).then(result => { if (!ignore) { setBio(result); } }); return () => { ignore = true; } }, [person]); return ( <> <select value={person} onChange={e => { setPerson(e.target.value); }}> <option value="Alice">Alice</option> <option value="Bob">Bob</option> <option value="Taylor">Taylor</option> </select> <hr /> <p><i>{bio ?? 'ஏற்றுகிறது...'}</i></p> </> ); }
async / await syntax பயன்படுத்தியும் rewrite செய்யலாம்; ஆனால் cleanup function இன்னும் provide செய்ய வேண்டும்:
import { useState, useEffect } from 'react'; import { fetchBio } from './api.js'; export default function Page() { const [person, setPerson] = useState('Alice'); const [bio, setBio] = useState(null); useEffect(() => { async function startFetching() { setBio(null); const result = await fetchBio(person); if (!ignore) { setBio(result); } } let ignore = false; startFetching(); return () => { ignore = true; } }, [person]); return ( <> <select value={person} onChange={e => { setPerson(e.target.value); }}> <option value="Alice">Alice</option> <option value="Bob">Bob</option> <option value="Taylor">Taylor</option> </select> <hr /> <p><i>{bio ?? 'ஏற்றுகிறது...'}</i></p> </> ); }
Data fetching-ஐ நேரடியாக Effects-இல் எழுதுவது repetitive ஆகும்; பின்னர் caching மற்றும் server rendering போன்ற optimizations சேர்ப்பதையும் கடினமாக்கும். உங்கள் சொந்த custom Hook-ஆக இருந்தாலும் அல்லது community maintained Hook-ஆக இருந்தாலும் அதை use செய்வது மேம்படும்.
Deep Dive
Effects-க்குள் fetch calls எழுதுவது, குறிப்பாக முழுமையாக client-side apps-இல், data fetch செய்வதற்கான popular வழி. ஆனால் இது மிகவும் manual approach; மேலும் குறிப்பிடத்தக்க downsides உள்ளன:
- Effects server-இல் run ஆகாது. இதன் பொருள் initial server-rendered HTML data இல்லாத loading state மட்டும் கொண்டிருக்கும். Client computer எல்லா JavaScript-ஐயும் download செய்து app-ஐ render செய்த பிறகே இப்போது data load செய்ய வேண்டும் என்பதை கண்டறியும். இது efficient அல்ல.
- Effects-இல் நேரடியாக fetching செய்வது “network waterfalls” உருவாக்க உதவுகிறது. நீங்கள் parent component-ஐ render செய்கிறீர்கள்; அது சில data fetch செய்கிறது; child components-ஐ render செய்கிறது; பின்னர் அவை தங்கள் data fetch செய்ய தொடங்குகின்றன. Network வேகமாக இல்லையெனில், எல்லா data-யையும் parallel-ஆக fetch செய்வதை விட இது குறிப்பிடத்தக்க அளவு slow.
- Effects-இல் நேரடியாக fetching செய்வது பொதுவாக data preload அல்லது cache செய்யப்படாது என்பதைக் குறிக்கும். உதாரணமாக, component unmount ஆகி மீண்டும் mount ஆனால், data-ஐ மீண்டும் fetch செய்ய வேண்டியிருக்கும்.
- இது மிகவும் ergonomic அல்ல. Race conditions போன்ற bugs ஏற்படாத வகையில்
fetchcalls எழுதும்போது கணிசமான boilerplate code தேவைப்படும்.
இந்த downsides list React-க்கு மட்டும் குறிப்பானது அல்ல. எந்த library-யுடனும் mount போது data fetch செய்வதற்கு இது பொருந்தும். Routing போலவே, data fetching-ஐ நன்றாக செய்வது trivial அல்ல; எனவே பின்வரும் approaches-ஐ பரிந்துரைக்கிறோம்:
- நீங்கள் framework use செய்தால், அதன் built-in data fetching mechanism-ஐ use செய்யுங்கள். Modern React frameworks efficient ஆன, மேலுள்ள pitfalls இல்லாத integrated data fetching mechanisms கொண்டுள்ளன.
- இல்லையெனில், client-side cache use செய்வதையோ build செய்வதையோ consider செய்யுங்கள். Popular open source solutions-இல் TanStack Query, useSWR, மற்றும் React Router 6.4+ அடங்கும். உங்கள் சொந்த solution-ஐயும் build செய்யலாம்; அப்போது under the hood Effects use செய்வீர்கள், ஆனால் requests-ஐ deduplicate செய்தல், responses-ஐ cache செய்தல், மற்றும் network waterfalls தவிர்த்தல் (data preload செய்வதன் மூலம் அல்லது data requirements-ஐ routes-க்கு hoist செய்வதன் மூலம்) ஆகிய logic சேர்ப்பீர்கள்.
இந்த approaches எதுவும் உங்களுக்கு பொருந்தவில்லை என்றால், Effects-இல் நேரடியாக data fetch செய்வதைத் தொடரலாம்.
Reactive dependencies specify செய்தல்
உங்கள் Effect-ன் dependencies-ஐ நீங்கள் “தேர்வு” செய்ய முடியாது என்பதை கவனியுங்கள். உங்கள் Effect code use செய்யும் ஒவ்வொரு reactive value-உம் dependency ஆக declare செய்யப்பட வேண்டும். உங்கள் Effect-ன் dependency list surrounding code மூலம் determined ஆகிறது:
function ChatRoom({ roomId }) { // இது reactive value
const [serverUrl, setServerUrl] = useState('https://localhost:1234'); // இதுவும் reactive value
useEffect(() => {
const connection = createConnection(serverUrl, roomId); // இந்த Effect இந்த reactive values-ஐ read செய்கிறது
connection.connect();
return () => connection.disconnect();
}, [serverUrl, roomId]); // ✅ எனவே அவற்றை உங்கள் Effect-ன் dependencies ஆக specify செய்ய வேண்டும்
// ...
}serverUrl அல்லது roomId ஏதாவது மாறினால், உங்கள் Effect புதிய values பயன்படுத்தி chat-க்கு reconnect செய்யும்.
Reactive values-இல் props மற்றும் உங்கள் component-க்குள் நேரடியாக declared செய்யப்பட்ட அனைத்து variables மற்றும் functions அடங்கும். roomId மற்றும் serverUrl reactive values என்பதால், அவற்றை dependencies-இலிருந்து remove செய்ய முடியாது. அவற்றை omit செய்ய முயற்சித்து உங்கள் linter React-க்காக சரியாக configured செய்யப்பட்டிருந்தால், இதை fix செய்ய வேண்டிய தவறாக linter flag செய்யும்:
function ChatRoom({ roomId }) {
const [serverUrl, setServerUrl] = useState('https://localhost:1234');
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.connect();
return () => connection.disconnect();
}, []); // 🔴 React Hook useEffect-க்கு missing dependencies உள்ளன: 'roomId' மற்றும் 'serverUrl'
// ...
}Dependency ஒன்றை remove செய்ய, அது dependency ஆக இருக்க தேவையில்லை என்பதை linter-க்கு “prove” செய்ய வேண்டும். உதாரணமாக, serverUrl reactive அல்ல மற்றும் re-renders போது மாறாது என்பதை prove செய்ய, அதை உங்கள் component-க்கு வெளியே move செய்யலாம்:
const serverUrl = 'https://localhost:1234'; // இனி reactive value அல்ல
function ChatRoom({ roomId }) {
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.connect();
return () => connection.disconnect();
}, [roomId]); // ✅ எல்லா dependencies-உம் declared
// ...
}இப்போது serverUrl reactive value அல்ல (மற்றும் re-render போது மாற முடியாது), அது dependency ஆக இருக்க தேவையில்லை. உங்கள் Effect code எந்த reactive values-யும் use செய்யவில்லை என்றால், அதன் dependency list empty ([]) ஆக இருக்க வேண்டும்:
const serverUrl = 'https://localhost:1234'; // இனி reactive value அல்ல
const roomId = 'music'; // இனி reactive value அல்ல
function ChatRoom() {
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.connect();
return () => connection.disconnect();
}, []); // ✅ எல்லா dependencies-உம் declared
// ...
}Empty dependencies கொண்ட Effect உங்கள் component-ன் props அல்லது state ஏதாவது மாறும்போது re-run ஆகாது.
Example 1 of 3: Dependency array pass செய்தல்
Dependencies-ஐ specify செய்தால், உங்கள் Effect initial commit-க்கு பிறகும் மற்றும் dependencies மாறிய commits-க்கு பிறகும் run ஆகும்.
useEffect(() => {
// ...
}, [a, b]); // a அல்லது b வேறுபட்டால் மீண்டும் run ஆகும்கீழுள்ள example-இல், serverUrl மற்றும் roomId reactive values; எனவே இரண்டும் dependencies ஆக specify செய்யப்பட வேண்டும். அதன் விளைவாக, dropdown-இல் வேறு room தேர்வு செய்தாலும் அல்லது server URL input edit செய்தாலும் chat re-connect ஆகும். ஆனால் message Effect-இல் use செய்யப்படாததால் (அதனால் dependency அல்ல), message edit செய்தால் chat re-connect ஆகாது.
import { useState, useEffect } from 'react'; import { createConnection } from './chat.js'; function ChatRoom({ roomId }) { const [serverUrl, setServerUrl] = useState('https://localhost:1234'); const [message, setMessage] = useState(''); useEffect(() => { const connection = createConnection(serverUrl, roomId); connection.connect(); return () => { connection.disconnect(); }; }, [serverUrl, roomId]); return ( <> <label> Server URL:{' '} <input value={serverUrl} onChange={e => setServerUrl(e.target.value)} /> </label> <h1>{roomId} அறைக்கு வரவேற்கிறோம்!</h1> <label> உங்கள் message:{' '} <input value={message} onChange={e => setMessage(e.target.value)} /> </label> </> ); } export default function App() { const [show, setShow] = useState(false); const [roomId, setRoomId] = useState('general'); return ( <> <label> Chat room-ஐ தேர்வு செய்க:{' '} <select value={roomId} onChange={e => setRoomId(e.target.value)} > <option value="general">பொது</option> <option value="travel">பயணம்</option> <option value="music">இசை</option> </select> <button onClick={() => setShow(!show)}> {show ? 'Chat-ஐ மூடு' : 'Chat-ஐ திற'} </button> </label> {show && <hr />} {show && <ChatRoom roomId={roomId}/>} </> ); }
Effect-இலிருந்து previous state அடிப்படையில் state update செய்தல்
Effect-இலிருந்து previous state அடிப்படையில் state update செய்ய விரும்பும்போது, ஒரு பிரச்சினை வரலாம்:
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
const intervalId = setInterval(() => {
setCount(count + 1); // Counter-ஐ ஒவ்வொரு second-க்கும் increment செய்ய விரும்புகிறீர்கள்...
}, 1000)
return () => clearInterval(intervalId);
}, [count]); // 🚩 ...ஆனால் `count`-ஐ dependency ஆக specify செய்தால் interval எப்போதும் reset ஆகும்.
// ...
}count reactive value என்பதால், அது dependencies list-இல் specify செய்யப்பட வேண்டும். ஆனால் count மாறும் ஒவ்வொரு முறையும் Effect cleanup செய்து மீண்டும் setup செய்ய இது காரணமாகிறது. இது ideal அல்ல.
இதை fix செய்ய, setCount-க்கு c => c + 1 state updater-ஐ pass செய்யுங்கள்:
import { useState, useEffect } from 'react'; export default function Counter() { const [count, setCount] = useState(0); useEffect(() => { const intervalId = setInterval(() => { setCount(c => c + 1); // ✅ State updater-ஐ pass செய்க }, 1000); return () => clearInterval(intervalId); }, []); // ✅ இப்போது count dependency அல்ல return <h1>{count}</h1>; }
இப்போது count + 1 பதிலாக c => c + 1 pass செய்வதால், உங்கள் Effect இனி count-ஐ depend செய்ய வேண்டியதில்லை. இந்த fix-ன் விளைவாக, count மாறும் ஒவ்வொரு முறையும் interval-ஐ cleanup செய்து setup செய்ய தேவையில்லை.
தேவையற்ற object dependencies-ஐ நீக்குதல்
உங்கள் Effect rendering போது create செய்யப்பட்ட object அல்லது function ஒன்றை depend செய்தால், அது மிக அடிக்கடி run ஆகலாம். உதாரணமாக, options object ஒவ்வொரு render-க்கும் வேறுபடுவதால் இந்த Effect ஒவ்வொரு commit-க்கும் பிறகு re-connect ஆகிறது:
const serverUrl = 'https://localhost:1234';
function ChatRoom({ roomId }) {
const [message, setMessage] = useState('');
const options = { // 🚩 இந்த object ஒவ்வொரு re-render-க்கும் scratch-இலிருந்து create செய்யப்படுகிறது
serverUrl: serverUrl,
roomId: roomId
};
useEffect(() => {
const connection = createConnection(options); // இது Effect-க்குள் use செய்யப்படுகிறது
connection.connect();
return () => connection.disconnect();
}, [options]); // 🚩 அதன் விளைவாக, இந்த dependencies ஒவ்வொரு commit-இலும் எப்போதும் வேறுபடும்
// ...Rendering போது create செய்யப்பட்ட object-ஐ dependency ஆக use செய்வதை தவிர்க்கவும். அதற்கு பதிலாக, object-ஐ Effect-க்குள் create செய்யுங்கள்:
import { useState, useEffect } from 'react'; import { createConnection } from './chat.js'; const serverUrl = 'https://localhost:1234'; function ChatRoom({ roomId }) { const [message, setMessage] = useState(''); useEffect(() => { const options = { serverUrl: serverUrl, roomId: roomId }; const connection = createConnection(options); connection.connect(); return () => connection.disconnect(); }, [roomId]); return ( <> <h1>{roomId} அறைக்கு வரவேற்கிறோம்!</h1> <input value={message} onChange={e => setMessage(e.target.value)} /> </> ); } export default function App() { const [roomId, setRoomId] = useState('general'); return ( <> <label> Chat room-ஐ தேர்வு செய்க:{' '} <select value={roomId} onChange={e => setRoomId(e.target.value)} > <option value="general">பொது</option> <option value="travel">பயணம்</option> <option value="music">இசை</option> </select> </label> <hr /> <ChatRoom roomId={roomId} /> </> ); }
இப்போது options object-ஐ Effect-க்குள் create செய்வதால், Effect தானாகவே roomId string-ஐ மட்டும் depend செய்கிறது.
இந்த fix உடன், input-இல் type செய்வது chat-ஐ reconnect செய்யாது. Re-created ஆகும் object போல அல்லாமல், roomId போன்ற string-ஐ வேறு value-ஆக set செய்யாவிட்டால் அது மாறாது. Dependencies remove செய்வது பற்றி மேலும் படிக்கவும்.
தேவையற்ற function dependencies-ஐ நீக்குதல்
உங்கள் Effect rendering போது create செய்யப்பட்ட object அல்லது function ஒன்றை depend செய்தால், அது மிக அடிக்கடி run ஆகலாம். உதாரணமாக, createOptions function ஒவ்வொரு render-க்கும் வேறுபடுவதால் இந்த Effect ஒவ்வொரு commit-க்கும் பிறகு re-connect ஆகிறது:
function ChatRoom({ roomId }) {
const [message, setMessage] = useState('');
function createOptions() { // 🚩 இந்த function ஒவ்வொரு re-render-க்கும் scratch-இலிருந்து create செய்யப்படுகிறது
return {
serverUrl: serverUrl,
roomId: roomId
};
}
useEffect(() => {
const options = createOptions(); // இது Effect-க்குள் use செய்யப்படுகிறது
const connection = createConnection();
connection.connect();
return () => connection.disconnect();
}, [createOptions]); // 🚩 அதன் விளைவாக, இந்த dependencies ஒவ்வொரு commit-இலும் எப்போதும் வேறுபடும்
// ...ஒவ்வொரு re-render-க்கும் function ஒன்றை scratch-இலிருந்து create செய்வது தனியாக ஒரு problem அல்ல. அதை optimize செய்ய தேவையில்லை. ஆனால் அதை உங்கள் Effect-ன் dependency ஆக use செய்தால், அது ஒவ்வொரு commit-க்கும் பிறகு உங்கள் Effect re-run ஆக காரணமாகும்.
Rendering போது create செய்யப்பட்ட function-ஐ dependency ஆக use செய்வதை தவிர்க்கவும். அதற்கு பதிலாக, அதை Effect-க்குள் declare செய்யுங்கள்:
import { useState, useEffect } from 'react'; import { createConnection } from './chat.js'; const serverUrl = 'https://localhost:1234'; function ChatRoom({ roomId }) { const [message, setMessage] = useState(''); useEffect(() => { function createOptions() { return { serverUrl: serverUrl, roomId: roomId }; } const options = createOptions(); const connection = createConnection(options); connection.connect(); return () => connection.disconnect(); }, [roomId]); return ( <> <h1>{roomId} அறைக்கு வரவேற்கிறோம்!</h1> <input value={message} onChange={e => setMessage(e.target.value)} /> </> ); } export default function App() { const [roomId, setRoomId] = useState('general'); return ( <> <label> Chat room-ஐ தேர்வு செய்க:{' '} <select value={roomId} onChange={e => setRoomId(e.target.value)} > <option value="general">பொது</option> <option value="travel">பயணம்</option> <option value="music">இசை</option> </select> </label> <hr /> <ChatRoom roomId={roomId} /> </> ); }
இப்போது createOptions function-ஐ Effect-க்குள் define செய்வதால், Effect தானாகவே roomId string-ஐ மட்டும் depend செய்கிறது. இந்த fix உடன், input-இல் type செய்வது chat-ஐ reconnect செய்யாது. Re-created ஆகும் function போல அல்லாமல், roomId போன்ற string-ஐ வேறு value-ஆக set செய்யாவிட்டால் அது மாறாது. Dependencies remove செய்வது பற்றி மேலும் படிக்கவும்.
Effect-இலிருந்து latest props மற்றும் state படித்தல்
Default ஆக, Effect-இலிருந்து reactive value ஒன்றை read செய்தால், அதை dependency ஆக add செய்ய வேண்டும். அந்த value-ன் ஒவ்வொரு change-க்கும் உங்கள் Effect “react” செய்யும் என்பதை இது உறுதி செய்கிறது. பெரும்பாலான dependencies-க்கு, இதுவே நீங்கள் விரும்பும் behavior.
ஆனால் சில நேரங்களில், அவற்றுக்கு “react” செய்யாமல் Effect-இலிருந்து latest props மற்றும் state-ஐ read செய்ய நீங்கள் விரும்புவீர்கள். உதாரணமாக, ஒவ்வொரு page visit-க்கும் shopping cart-இல் உள்ள items எண்ணிக்கையை log செய்ய விரும்புகிறீர்கள் என்று கற்பனை செய்யுங்கள்:
function Page({ url, shoppingCart }) {
useEffect(() => {
logVisit(url, shoppingCart.length);
}, [url, shoppingCart]); // ✅ எல்லா dependencies-உம் declared
// ...
}ஒவ்வொரு url change-க்கும் பிறகு புதிய page visit log செய்ய வேண்டும், ஆனால் shoppingCart மட்டும் மாறினால் வேண்டாம் என்றால்? Reactivity rules-ஐ break செய்யாமல் shoppingCart-ஐ dependencies-இலிருந்து exclude செய்ய முடியாது. ஆனால் Effect-க்குள் இருந்து call செய்யப்பட்டாலும், code-ன் ஒரு பகுதி changes-க்கு “react” செய்ய வேண்டாம் என்று express செய்யலாம். useEffectEvent Hook மூலம் Effect Event declare செய்து, shoppingCart read செய்யும் code-ஐ அதற்குள் move செய்யுங்கள்:
function Page({ url, shoppingCart }) {
const onVisit = useEffectEvent(visitedUrl => {
logVisit(visitedUrl, shoppingCart.length)
});
useEffect(() => {
onVisit(url);
}, [url]); // ✅ எல்லா dependencies-உம் declared
// ...
}Effect Events reactive அல்ல; அவை உங்கள் Effect-ன் dependencies-இலிருந்து எப்போதும் omit செய்யப்பட வேண்டும். இதுவே non-reactive code-ஐ (சில props மற்றும் state-ன் latest value-ஐ read செய்யும் இடம்) அவற்றுக்குள் வைக்க அனுமதிக்கிறது. onVisit-க்குள் shoppingCart read செய்வதன் மூலம், shoppingCart உங்கள் Effect-ஐ re-run செய்யாது என்பதை உறுதி செய்கிறீர்கள்.
Server மற்றும் client-இல் வேறுபட்ட content display செய்தல்
உங்கள் app server rendering use செய்தால் (நேரடியாக அல்லது framework மூலம்), உங்கள் component இரண்டு வேறுபட்ட environments-இல் render ஆகும். Server-இல், initial HTML produce செய்ய அது render ஆகும். Client-இல், அந்த HTML-க்கு உங்கள் event handlers attach செய்ய React rendering code-ஐ மீண்டும் run செய்யும். அதனால்தான் hydration வேலை செய்ய, உங்கள் initial render output client மற்றும் server-இல் identical ஆக இருக்க வேண்டும்.
அரிதான cases-இல், client-இல் வேறுபட்ட content display செய்ய வேண்டியிருக்கலாம். உதாரணமாக, உங்கள் app localStorage-இலிருந்து சில data read செய்தால், server-இல் அதை செய்ய முடியாது. இதை இவ்வாறு implement செய்யலாம்:
function MyComponent() {
const [didMount, setDidMount] = useState(false);
useEffect(() => {
setDidMount(true);
}, []);
if (didMount) {
// ... return client-only JSX ...
} else {
// ... return initial JSX ...
}
}App loading ஆகும் போது, user initial render output-ஐ பார்ப்பார். பின்னர் அது loaded மற்றும் hydrated ஆனதும், உங்கள் Effect run ஆகி didMount-ஐ true ஆக set செய்து re-render trigger செய்யும். இது client-only render output-க்கு switch செய்யும். Effects server-இல் run ஆகாததால், initial server render போது didMount false ஆக இருந்தது இதனால்தான்.
இந்த pattern-ஐ குறைவாக use செய்யுங்கள். Slow connection கொண்ட users initial content-ஐ கணிசமான நேரம்—சில seconds கூட—பார்ப்பார்கள் என்பதை நினைவில் கொள்ளுங்கள்; எனவே உங்கள் component appearance-இல் jarring changes செய்ய விரும்பமாட்டீர்கள். பல cases-இல், CSS மூலம் conditionally வேறுபட்ட விஷயங்களை காட்டி இதற்கான தேவையை தவிர்க்கலாம்.
Troubleshooting
Component mount ஆகும்போது என் Effect இருமுறை run ஆகிறது
Strict Mode on ஆக இருந்தால், development-இல் actual setup-க்கு முன் React setup மற்றும் cleanup-ஐ கூடுதலாக ஒருமுறை run செய்கிறது.
உங்கள் Effect-ன் logic சரியாக implemented உள்ளதா என்பதை verify செய்யும் stress-test இது. இது visible issues ஏற்படுத்தினால், உங்கள் cleanup function-இல் சில logic missing. Cleanup function setup function செய்ததை stop அல்லது undo செய்ய வேண்டும். Rule of thumb: setup ஒருமுறை call செய்யப்படுவது (production போல) மற்றும் setup → cleanup → setup sequence (development போல) இடையே user வேறுபாடு காணக்கூடாது.
இது bugs கண்டுபிடிக்க எப்படி உதவுகிறது மற்றும் உங்கள் logic-ஐ எப்படி fix செய்வது பற்றி மேலும் படிக்கவும்.
ஒவ்வொரு re-render-க்கும் பிறகு என் Effect run ஆகிறது
முதலில், dependency array specify செய்ய மறந்துவிடவில்லை என்பதை check செய்யுங்கள்:
useEffect(() => {
// ...
}); // 🚩 Dependency array இல்லை: ஒவ்வொரு commit-க்கும் பிறகு re-run ஆகும்!Dependency array specify செய்திருந்தும் உங்கள் Effect loop-இல் re-run ஆகிக் கொண்டிருந்தால், உங்கள் dependencies-இல் ஒன்று ஒவ்வொரு re-render-க்கும் வேறுபடுவதால்தான்.
உங்கள் dependencies-ஐ console-க்கு manual ஆக log செய்து இந்த problem-ஐ debug செய்யலாம்:
useEffect(() => {
// ..
}, [serverUrl, roomId]);
console.log([serverUrl, roomId]);பின்னர் console-இல் வேறுபட்ட re-renders-இலிருந்து வந்த arrays-ஐ right-click செய்து இரண்டிற்கும் “Store as a global variable” select செய்யலாம். முதல் ஒன்று temp1 ஆகவும் இரண்டாவது ஒன்று temp2 ஆகவும் save ஆனதாகக் கொண்டு, இரு arrays-இலுள்ள ஒவ்வொரு dependency-யும் same ஆக உள்ளதா என்பதை browser console மூலம் check செய்யலாம்:
Object.is(temp1[0], temp2[0]); // arrays இடையே முதல் dependency same ஆக உள்ளதா?
Object.is(temp1[1], temp2[1]); // arrays இடையே இரண்டாவது dependency same ஆக உள்ளதா?
Object.is(temp1[2], temp2[2]); // ... ஒவ்வொரு dependency-க்கும் இதேபோல் ...ஒவ்வொரு re-render-க்கும் வேறுபடும் dependency-ஐ கண்டுபிடித்ததும், பொதுவாக பின்வரும் வழிகளில் ஒன்றால் அதை fix செய்யலாம்:
- Effect-இலிருந்து previous state அடிப்படையில் state update செய்தல்
- தேவையற்ற object dependencies-ஐ நீக்குதல்
- தேவையற்ற function dependencies-ஐ நீக்குதல்
- Effect-இலிருந்து latest props மற்றும் state படித்தல்
Last resort ஆக (இந்த methods உதவவில்லை என்றால்), அதன் creation-ஐ useMemo அல்லது (functions-க்கு) useCallback மூலம் wrap செய்யுங்கள்.
என் Effect infinite cycle-இல் தொடர்ந்து re-run ஆகிறது
உங்கள் Effect infinite cycle-இல் run ஆனால், இந்த இரண்டு விஷயங்களும் உண்மை ஆக வேண்டும்:
- உங்கள் Effect ஏதாவது state-ஐ update செய்கிறது.
- அந்த state re-render-க்கு வழிவகுக்கிறது; அது Effect-ன் dependencies மாற காரணமாகிறது.
Problem-ஐ fix செய்யத் தொடங்குவதற்கு முன், உங்கள் Effect DOM, network, third-party widget போன்ற external system ஒன்றுடன் connect செய்கிறதா என்று உங்களையே கேளுங்கள். உங்கள் Effect ஏன் state set செய்ய வேண்டும்? அது அந்த external system உடன் synchronize செய்கிறதா? அல்லது உங்கள் application’s data flow-ஐ அதன்மூலம் manage செய்ய முயற்சிக்கிறீர்களா?
External system இல்லையெனில், Effect-ஐ முழுமையாக remove செய்வது உங்கள் logic-ஐ simplify செய்யுமா என்று consider செய்யுங்கள்.
நீங்கள் உண்மையாக external system ஒன்றுடன் synchronize செய்கிறீர்கள் என்றால், உங்கள் Effect ஏன் மற்றும் எந்த conditions-இல் state update செய்ய வேண்டும் என்று சிந்தியுங்கள். உங்கள் component-ன் visual output-ஐ பாதிக்கும் ஏதாவது மாறியுள்ளதா? Rendering-இல் use செய்யப்படாத data ஒன்றை track செய்ய வேண்டுமெனில், re-renders trigger செய்யாத ref இன்னும் பொருத்தமாக இருக்கலாம். உங்கள் Effect தேவையானதை விட அதிகமாக state update செய்து re-renders trigger செய்யவில்லை என்பதை verify செய்யுங்கள்.
இறுதியாக, உங்கள் Effect சரியான நேரத்தில் state update செய்தாலும் loop இன்னும் இருந்தால், அந்த state update Effect-ன் dependencies-இல் ஒன்றை மாற்றுவதால்தான். Dependency changes-ஐ debug செய்வது எப்படி என்பதை படிக்கவும்.
என் component unmount ஆகாதிருந்தாலும் cleanup logic run ஆகிறது
Cleanup function unmount போது மட்டும் அல்ல, dependencies மாறிய ஒவ்வொரு re-render-க்கும் முன்பும் run ஆகும். மேலும் development-இல், component mount ஆன உடனே React setup+cleanup-ஐ கூடுதலாக ஒருமுறை run செய்கிறது.
Corresponding setup code இல்லாமல் cleanup code இருந்தால், அது பொதுவாக code smell:
useEffect(() => {
// 🔴 Avoid: Cleanup logic without corresponding setup logic
return () => {
doSomething();
};
}, []);உங்கள் cleanup logic setup logic-க்கு “symmetrical” ஆக இருக்க வேண்டும்; setup செய்ததை stop அல்லது undo செய்ய வேண்டும்:
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.connect();
return () => {
connection.disconnect();
};
}, [serverUrl, roomId]);Effect lifecycle component lifecycle-இலிருந்து எப்படி வேறுபடுகிறது என்பதை அறிக.
என் Effect visual ஏதாவது செய்கிறது; அது run ஆகும் முன் flicker தெரிகிறது
உங்கள் Effect browser screen-ஐ paint செய்வதை block செய்ய வேண்டும் என்றால், useEffect-ஐ useLayoutEffect-ஆக மாற்றுங்கள். பெரும்பாலான Effects-க்கு இது தேவையில்லை என்பதை கவனிக்கவும். Browser paint-க்கு முன் உங்கள் Effect run ஆகுவது மிக முக்கியமானபோது மட்டும் இது தேவைப்படும்: உதாரணமாக, user பார்க்கும் முன் tooltip-ஐ measure செய்து position செய்ய.