Why does my useEffect run twice in development?
In Strict Mode, React 18 and later deliberately mounts, unmounts and remounts each component once in development to check that effects clean up properly. It does not happen in production.
Explain it simply
A shopkeeper tests the shutter by pulling it down and up once before opening. React Strict Mode does the same: mount, run cleanup, mount again. If your effect survives that, it will also survive a real user navigating away and coming back.
If you see a doubled network call or a doubled subscription, the effect is missing its cleanup. React is not misbehaving; it is showing you a bug that real users would hit when they switch screens quickly.
This only happens in development. A production build runs the effect once per mount, so do not write code that depends on the extra run, and do not remove StrictMode just to make the log look quieter.
Example
useEffect(() => {
const ws = new WebSocket(url);
return () => ws.close(); // needed: the first connection is closed in dev
}, [url]);For one-time work that must not repeat, such as sending an analytics event, make it safe to run twice or trigger it from an event handler instead of an effect.
What to say in the interview
"Strict Mode intentionally re-runs effects once in development to surface missing cleanup. Production runs them once. The fix is a proper cleanup function, not removing StrictMode or adding a ref hack to skip the second run."
Common mistakes
- Disabling StrictMode to hide the double call.