Back to More
React Deep Dive
useEffect — The Complete Picture
The hook everyone uses. The hook almost everyone misuses. Let us fix that.
When Does useEffect Run?
| Dependency Array | Runs |
|---|---|
| undefined | After every render (avoid this) |
| [] | Only on mount (first render) |
| [a, b] | On mount + whenever a or b change |
The Cleanup Function
If your effect sets up something (event listener, interval, subscription), clean it up. The function you return from useEffect runs when the component unmounts or before the effect re-runs.
useEffect(() => {
const timer = setInterval(() => setCount(c => c + 1), 1000);
return () => clearInterval(timer); // ✅ cleanup
}, []);Common Mistakes
❌ Missing dependencies
useEffect(() => {
fetch(`/api/user/${userId}`);
}, []); // userId changes but effect never re-runs✅ Correct
useEffect(() => {
fetch(`/api/user/${userId}`);
}, [userId]); // re-fetches when userId changesDo You Even Need useEffect?
You might not. React 18+ has better alternatives:
- Derived state —
const fullName = firstName + " " + lastName;— no effect needed. - Event handlers — Fetch data when the user clicks, not on mount.
- Custom hooks —
useQueryfrom TanStack Query handles fetching + caching + loading states.
TL;DR
1. Always declare the dependency array.
2. Always clean up subscriptions.
3. If you can compute it from existing state/props, do not use useEffect.