TK
All writing
Frontend

useEffect: the mistakes everyone makes

React's useEffect is the hook people misuse most. Missing dependencies, infinite loops, the cleanup function, race conditions in data fetching, and when you shouldn't use an effect at all.

Tanveer Khan10 min read

useEffect is the hook that causes the most bugs, usually because it's reached for when it isn't needed, or wired up without cleanup. Here are the failure modes, in the order they bite.

The infinite loop

An effect that sets state it also depends on re-runs forever. The classic: updating state with no dependency array, or depending on an object/array recreated every render.

loop.tsx
tsx
// ❌ runs every render → setState → re-render → runs again...
useEffect(() => {
  setCount(count + 1);
});

// ✅ run once on mount
useEffect(() => {
  setCount((c) => c + 1);
}, []);

Missing dependencies

The dependency array must list every reactive value the effect reads. Omit one and the effect uses a stale value; the eslint react-hooks plugin catches these — don't silence it, fix it.

Cleanup: the part people skip

cleanup.tsx
tsx
useEffect(() => {
  const id = setInterval(tick, 1000);
  const onResize = () => measure();
  window.addEventListener("resize", onResize);

  // cleanup runs on unmount AND before the next effect run
  return () => {
    clearInterval(id);
    window.removeEventListener("resize", onResize);
  };
}, []);

Race conditions in data fetching

Fetch in an effect and the user navigates fast? An older request can resolve AFTER a newer one and overwrite fresh data with stale. Guard with an ignore flag (or AbortController).

race.tsx
tsx
useEffect(() => {
  let ignore = false;
  fetchUser(id).then((u) => {
    if (!ignore) setUser(u); // drop the result if we've moved on
  });
  return () => { ignore = true; };
}, [id]);

Often you don't need an effect at all

Transforming props/state for render? Do it during render, not in an effect. Responding to a user event? Do it in the event handler. Effects are for synchronising with something OUTSIDE React — the DOM, a subscription, a timer, the network — not for reacting to your own state.

Before writing an effect, ask: am I syncing with an external system? If not, there's usually a simpler place for the logic. When you do need one, list all deps and return a cleanup. That alone removes most useEffect bugs.