Common Mistakes with useEffect in React and How to Avoid Them
useEffect include missing dependencies in the dependency array, causing effects to run too often or not at all, and creating infinite loops by updating state inside the effect without proper conditions. Always include all variables your effect uses in the dependency array and avoid side effects that cause continuous re-renders.Syntax
The useEffect hook runs side effects in React components. It takes two arguments: a function to run the effect, and an optional dependency array that controls when the effect runs.
- Effect function: Code to run after render, like fetching data or updating the DOM.
- Dependency array: List of variables that trigger the effect when they change. If empty, effect runs once after first render.
useEffect(() => {
// effect code here
return () => {
// cleanup code here (optional)
};
}, [dependencies]);Example
This example shows a counter that updates the document title using useEffect. The effect runs only when the counter changes because the counter is in the dependency array.
import React, { useState, useEffect } from 'react'; function Counter() { const [count, setCount] = useState(0); useEffect(() => { document.title = `Count: ${count}`; }, [count]); return ( <div> <p>Count is {count}</p> <button onClick={() => setCount(count + 1)}>Increment</button> </div> ); } export default Counter;
Common Pitfalls
1. Missing dependencies: Forgetting to list variables used inside the effect causes stale values or missed updates.
2. Infinite loops: Updating state inside useEffect without proper dependency control causes the effect to run endlessly.
3. Overusing effects: Running effects on every render by omitting the dependency array hurts performance.
4. Incorrect cleanup: Not cleaning up subscriptions or timers can cause memory leaks.
import React, { useState, useEffect } from 'react'; function WrongExample() { const [count, setCount] = useState(0); // Wrong: missing dependency array causes effect to run every render useEffect(() => { console.log('Effect runs every render'); }); // Wrong: updating state inside effect without proper condition causes infinite loop useEffect(() => { setCount(count + 1); }, [count]); return <p>Count: {count}</p>; } function FixedExample() { const [count, setCount] = useState(0); // Correct: effect runs only once useEffect(() => { console.log('Effect runs once'); }, []); // Correct: update state with condition or different dependency useEffect(() => { if (count < 5) { setCount(count + 1); } }, [count]); return <p>Count: {count}</p>; }
Quick Reference
- Always include all variables your effect uses in the dependency array.
- Use an empty array
[]to run effect only once after mount. - Clean up subscriptions or timers in the return function to avoid leaks.
- Avoid updating state inside
useEffectwithout conditions to prevent infinite loops. - Use React DevTools to check effect runs and dependencies.