Programming & Coding How-TosTechnology

useEffect Runs Twice in React 18? Why It Happens & Fixes

Admin
useEffect Runs Twice in React 18? Why It Happens & Fixes

You upgrade to React 18, run your app in development, and suddenly everything happens twice. Your API is called twice, your console log prints twice, and a chat connection seems to open, close, and open again. It looks like a bug in your code. In most cases, it is not.

React 18 introduced a deliberate behaviour in development mode that makes effects run, clean up, and run again. This guide explains exactly why that happens, how to check whether it affects your production build, and how to write effects that behave correctly under this stricter checking.

Quick Answer

In React 18, useEffect runs twice in development because StrictMode intentionally mounts your component, unmounts it, and remounts it to test whether your effects clean up properly. This double behaviour happens only in development and only inside <StrictMode>. Production builds are not affected, and the correct fix is to add proper cleanup functions rather than removing StrictMode.

What StrictMode Does in React 18

StrictMode is a wrapper component that turns on extra checks during development. It does not render anything visible and it does not change your production build. Its job is to surface weak spots in your code before users find them.

In React 18, StrictMode gained a new check often called strict effects. When a component first mounts, React simulates an unmount and remount cycle:

  1. React mounts the component and runs your effects.
  2. React runs the cleanup function of each effect, as if the component unmounted.
  3. React runs the effects again, as if the component remounted.

State and refs are preserved during this simulated cycle. From the outside, it looks like your effect ran twice in a row. In reality, React is asking a simple question: if this component disappeared and came back, would it still work correctly?

This matters because real apps unmount and remount components all the time. Users switch tabs, open and close modals, navigate away and come back. Features planned for React also rely on components surviving this cycle. Code that only works on a perfect first mount is fragile code.

How to Confirm StrictMode Is the Cause

Step 1: Check Your Entry File

Open your entry file, usually main.jsx, main.tsx, or index.js. Look for the wrapper around your app:

<React.StrictMode><App /></React.StrictMode>

Many project templates, including Vite and older Create React App setups, add this wrapper by default. If it is there and you are in development mode, double effects are expected.

Step 2: Test a Production Build

Build and preview the production version of your app. If the double calls disappear in production, StrictMode is confirmed as the cause and there is no user-facing bug. This single test saves many developers hours of chasing a problem that does not exist for real users.

Step 3: Temporarily Remove StrictMode to Compare

You can remove the StrictMode wrapper for a quick test. If effects now run once in development, you have found the source. Do not treat this as the final fix, though. Put StrictMode back and fix the effect, because the wrapper is protecting you from real bugs.

How to Write Effects That Survive the Double Run

Always Clean Up Subscriptions and Listeners

Anything an effect sets up outside React must be torn down in the cleanup function that the effect returns. Common examples include:

  • Event listeners added with addEventListener, removed with removeEventListener.
  • Timers started with setInterval or setTimeout, cleared in cleanup.
  • WebSocket or chat connections, closed in cleanup.
  • Subscriptions to external stores or libraries, unsubscribed in cleanup.

A typical pattern looks like this: useEffect(() => { window.addEventListener("resize", onResize); return () => window.removeEventListener("resize", onResize); }, []); When setup and cleanup are exact mirrors, the double run in development becomes harmless, and the component is also safe on real unmounts.

Make Data Fetching Ignore Stale Results

Fetching is where double effects are noticed most, because two network requests appear in the network tab. The requests themselves are not harmful in development, but the pattern reveals a real risk: an older, slower response can arrive after a newer one and overwrite fresh data.

Protect fetches with an ignore flag or an abort signal:

  1. Create a local flag such as let ignore = false; inside the effect.
  2. After the response arrives, set state only if ignore is still false.
  3. In cleanup, set ignore = true; so the stale run cannot update state.

Better still, use AbortController to cancel the request in cleanup, or use a data-fetching library that handles caching, deduplication, and cancellation for you. With that protection, whether the effect runs once or twice, the final state is always correct.

Avoid Side Effects That Cannot Be Repeated Safely

Some actions should not run inside an effect twice, or even inside an effect at all. Sending a payment, creating an order, posting a comment, or logging an analytics purchase event are actions, not synchronisation. They usually belong in an event handler, where they run once in direct response to a user action.

Ask yourself what the effect is really for. Effects are for synchronising your component with something outside React, such as a network connection, a browser API, or a third-party widget. If the code reacts to a specific click or form submission, move it out of the effect and into the handler. Our companion guide on how to stop an infinite re-render loop caused by setState inside useEffect covers the related mistakes that appear when effects and state updates are mixed carelessly.

Why Removing StrictMode Is the Wrong Fix

Removing StrictMode does make the symptom go away in development, but it hides the underlying weakness. The effect that double-fires an analytics event today will also double-fire when a parent component remounts it tomorrow. The connection that leaks under StrictMode will also leak in production when users navigate.

The team behind React explains this reasoning in the React documentation on StrictMode and its development checks, and the React documentation on useEffect setup and cleanup gives detailed examples for connections, fetching, and animations.

Think of StrictMode as a free test that runs on every mount. Passing that test means your components are ready for real navigation patterns and for future React features that preserve state across remounts.

Common Mistakes and Useful Tips

  • Panicking over two network calls in development. Check production first. If production is single, your users never saw the problem.
  • Using a global flag to block the second run. Tricks such as a module-level boolean break when the component genuinely unmounts and remounts, and they hide real leaks.
  • Forgetting cleanup for intervals. Without cleanup, the double mount leaves two intervals running, and values update twice as fast after every remount.
  • Counting on effect order or timing. Write effects that give the correct result no matter how many times setup and cleanup alternate.
  • Leaving StrictMode off permanently after a test. Restore it as soon as your comparison test is done, so future effects get the same protection.

One practical tip: when you see a doubled log, add the cleanup log too. Logging both setup and cleanup shows the full mount, cleanup, remount sequence and makes the StrictMode pattern obvious in seconds.

Conclusion

useEffect running twice in React 18 development is a feature, not a bug. StrictMode mounts, cleans up, and remounts your components to prove that your effects can survive real-world unmounts. Confirm it with a production build, keep StrictMode on, and fix the real issue by writing clean, reversible effects with proper cleanup and stale-response protection. Code that passes this check is code you can trust in production.

Frequently Asked Questions

Does the double effect happen in production?

No. The mount, unmount, remount simulation is a development-only check inside StrictMode. Production builds run each effect normally when dependencies change and clean up on unmount.

Should I remove StrictMode to stop the double calls?

No, except for a brief test to confirm the cause. Removing it hides missing cleanup and race conditions that will still cause bugs in production when components remount for other reasons.

Why does my API get called twice but my colleague’s does not?

Your colleague may not be using StrictMode, may be viewing a production build, or may fetch data through a caching library that deduplicates identical requests. The component behaviour is the same; the setup around it differs.

How do I stop duplicate analytics or log events?

Move one-time actions out of effects and into event handlers where they respond to a user action once. If an event truly belongs in an effect, guard it with proper cleanup and make the tracking call safe to repeat or easy to cancel.

Did React 17 behave this way?

React 17 StrictMode double-invoked render functions and some other methods, but it did not simulate the effect cleanup and re-run cycle. That specific double-effect behaviour was added in React 18.

Admin

Passionate about the intersection of style and storytelling. I simplify fashion trends and industry news into easy-to-follow advice, helping you stay stylish and informed in the fast-paced world of fashion.

Leave a Reply

Your email address will not be published. Required fields are marked *