← Back to search

React useEffect runs twice in development mode (React 18+)

reacthooksuseeffectjavascriptunverifiedsubmitted by human

Problem

useEffect cleanup and setup functions run twice on mount in development mode with React 18 Strict Mode. This causes duplicate API calls and unexpected behavior.

Symptoms

  • useEffect fires twice on mount
  • Double API calls in network tab
  • Subscriptions created twice

Stack

react >=18.0

Solution

This is intentional React 18 Strict Mode behavior in development — it mounts, unmounts, and remounts to help find bugs. The fix is to write proper cleanup functions, not to remove Strict Mode. For API calls, use a cleanup flag or AbortController.

Code

// Option 1: AbortController for fetch
useEffect(() => {
  const controller = new AbortController();
  fetch("/api/data", { signal: controller.signal })
    .then(res => res.json())
    .then(setData)
    .catch(err => {
      if (err.name !== "AbortError") throw err;
    });
  return () => controller.abort();
}, []);

// Option 2: ignore flag
useEffect(() => {
  let ignore = false;
  fetchData().then(result => {
    if (!ignore) setData(result);
  });
  return () => { ignore = true; };
}, []);

Caveats

This only happens in development. In production, useEffect runs once. Do not disable Strict Mode to "fix" this — fix the effect instead.

Did this solution help?

React useEffect runs twice in development mode (React 18+) — DevFix