useEffect runs twice in development — React StrictMode double-invoking effects

Reactjs·Sep 13, 2026·beginner·
Quick answer

In development, StrictMode intentionally mounts, unmounts, and remounts components once to surface Effects that aren't properly cleaned up — it's a diagnostic, not a bug, and it does not happen in production builds. If double-invocation causes visible problems (duplicate API calls, doubled state), the actual bug is a missing or incorrect cleanup function in useEffect.

What causes this error

StrictMode simulates unmounting and remounting a component immediately after its first mount, specifically to catch Effects whose setup isn't idempotent or whose cleanup is missing — problems that would otherwise only surface unpredictably in production under real remounts (like tab switching with certain routing setups). It only does this in development; production builds mount once as normal.

The Fix

// Wrong — no cleanup, so remounting leaves a duplicate subscription/interval
useEffect(() => {
  const id = setInterval(tick, 1000);
}, []);

// Fix — always return a cleanup function that undoes the setup
useEffect(() => {
  const id = setInterval(tick, 1000);
  return () => clearInterval(id);
}, []);

Resist the urge to remove <StrictMode> to make the symptom disappear — that hides the bug in development but doesn't fix the underlying issue, which can still bite you in production.

Common causes / variations

  • A `fetch` or subscription set up in `useEffect` with no corresponding `abort()`/`unsubscribe()` in the cleanup
  • An interval or timeout started without being cleared
  • Treating an Effect's setup as something that should only ever run once, ever, rather than being safely re-runnable

Related errors

See also: Cannot update a component while rendering a different component, Activity not preserving state.

Was this fix helpful?

Comments

Loading comments...