Join the discussion
Comments
Loading comments...
Cookie preferences
We use essential cookies for sign-in and preferences. With your permission, we also use basic analytics to improve lessons.
Join the discussion
Loading comments...
We use essential cookies for sign-in and preferences. With your permission, we also use basic analytics to improve lessons.
Effects kab chalte hain, cleanup ka timing aur dependency tracking.
useEffect React components ko external systems ke saath synchronize karne ka primary hook hai β browser APIs, third-party libraries, network requests, aur subscriptions. Yeh browser ke screen paint karne ke baad run hota hai, ise non-blocking banata hai. Har effect optionally ek cleanup function return karta hai jo React effect dobara run karne se pehle aur component unmount hone par call karta hai.
React DOM mutations commit karta hai, browser screen paint karta hai, aur phir React asynchronously aapke useEffect callbacks run karta hai. Yeh order matlab effects pehle render ko kabhi block nahi karte β user immediately content dekhta hai.
React current deps array ko Object.is() equality use karke previous render ke deps se compare karta hai. Agar koi dep change hua, effect re-run schedule hota hai. Koi deps array nahi = har render ke baad run. Empty array [] = mount ke baad ek baar run.
Agar aapka effect ek function return karta hai, React ise cleanup ke roop mein store karta hai. Effect dobara run karne se pehle (kyunki ek dep change hua), React pehle previous cleanup call karta hai. Component unmount hone par, React last cleanup call karta hai.
StrictMode mein, React intentionally har component ko mount β unmount β remount karta hai taaki woh effects surface ho sakein jo properly clean up nahi karte. Double invocation sirf development mein hoti hai. Isliye proper cleanup non-negotiable hai.
useLayoutEffect ke useEffect jaisi hi semantics hain lekin DOM mutations ke baad aur browser paint se pehle synchronously fire hota hai. Ise tab use karein jab aapke effect ko DOM measurements read karni hon aur user kuch dekhne se pehle corrections apply karni hon.
Re-run frequency control karta hai. [] = sirf mount. [a, b] = jab a ya b change ho. Koi array nahi = har render. React Object.is() se compare karta hai β objects/arrays ko deps ke roop mein avoid karein.
Aapke effect se return ki gayi function. Effect re-run se pehle (dep changed) aur component unmount hone par call hoti hai. Fetches cancel karne, timers clear karne, unsubscribe karne ke liye use hoti hai.
Jab ek effect purane render se state/props capture karta hai lekin updated values paane ke liye kabhi re-run nahi karta. Variable ko deps mein add karke ya ref use karke fix karein.
DOM mutation ke baad, paint se pehle synchronously fire hota hai. Painting block karta hai β ise fast rakhein. DOM measurements ke liye use karein (getBoundingClientRect, scroll position).
Kisi bhi DOM mutation se pehle fire hota hai. CSS-in-JS libraries ke liye intended hai taaki woh style rules synchronously inject kar sakein layout se pehle. General application code ke liye nahi.
1// 1. Event subscription β must clean up2useEffect(() => {3 window.addEventListener('resize', handleResize);4 return () => window.removeEventListener('resize', handleResize);5}, []); // mount once, clean up on unmount67// 2. Fetch with cancellation (AbortController)8useEffect(() => {9 const controller = new AbortController();1011 fetch('/api/data', { signal: controller.signal })12 .then(res => res.json())13 .then(setData)14 .catch(err => {15 if (err.name !== 'AbortError') throw err; // ignore cancellation16 });1718 return () => controller.abort(); // cancel if dep changes or unmount19}, [userId]);2021// 3. useLayoutEffect for DOM measurement22useLayoutEffect(() => {23 const height = ref.current.getBoundingClientRect().height;24 setContainerHeight(height); // applied before paint β no flicker25}, [content]);
useEffect React mein sabse zyada galat use hone wala hook hai. Effects sahi karna β correct deps, proper cleanup, sahi timing β aise apps banane ke liye essential hai jo memory leak nahi karte, extra network requests nahi karte, aur stale data bugs nahi rakhte. Mental model shift hai: 'effects synchronization mechanisms hain', 'lifecycle events nahi'. Aap React ko bata rahe hain outside world ke saath sync kaise rahein, mount/update/unmount callbacks mein hook karna nahi.
A developer writes `useEffect(() => { fetchData() })` without a dependency array. The app makes thousands of API calls per second, the browser freezes, and the API rate limits the user.
No dependency array means the effect runs AFTER EVERY RENDER. fetchData sets state β state change triggers re-render β re-render fires the effect β effect calls fetchData β sets state β infinite loop. Each loop iteration also creates a new network request.
Add the correct dependency array: `useEffect(() => { fetchData() }, [])` for mount-only, or `useEffect(() => { fetchData(query) }, [query])` for re-fetch when query changes. The dependency array tells React's effect scheduler when to skip re-execution.
Takeaway: The dependency array is not optional decoration β it's the core control mechanism of useEffect. No array = every render. Empty array = mount only. Specific deps = run when those values change. Getting this wrong is the #1 source of useEffect bugs.
A countdown timer displays the time remaining. You use `setInterval` inside useEffect with an empty dependency array. The timer shows the same number forever instead of counting down.
The useEffect callback closes over the initial `count` value (e.g., 60). setInterval calls `setCount(count - 1)` every second, but `count` is always 60 inside the closure (captured at mount time). Result: `setCount(59)` every second β never changes from 59.
Use the functional updater form: `setCount(prev => prev - 1)`. The updater receives the CURRENT state, not the stale closure value. Alternatively, store count in a ref and sync it every render. The functional form is React's escape hatch for stale closures in effects.
Takeaway: Effects capture values at the time they're created (closures). For intervals and timeouts that need current state, always use the functional updater `setState(prev => ...)` or a ref pattern to bypass the stale closure.