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 ArrayRuns
undefinedAfter 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 changes

Do You Even Need useEffect?

You might not. React 18+ has better alternatives:

  • Derived stateconst fullName = firstName + " " + lastName; — no effect needed.
  • Event handlers — Fetch data when the user clicks, not on mount.
  • Custom hooksuseQuery from 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.