← 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.