# React Strict Mode reruns effects to expose missing cleanup

> React Strict Mode runs an extra setup and cleanup cycle in development. Make effects reversible and requests deduplicated instead of hiding the signal.

Source: https://saascode.ai/inside/react-strict-double-mount · Published: 2026-08-21 · Section: academy

---
You add `useEffect(..., [])`, open the network panel, and see two requests. The tempting fix is a ref named `hasRun`. That can silence the symptom while preserving the bug Strict Mode found.

Several live builds had a translation preloader that fetched twice on every development mount. React was doing this deliberately. In Strict Mode, development runs an extra `setup → cleanup → setup` cycle to expose effects that cannot be safely stopped and restarted.

The goal is not “make the effect run once.” React's own guidance is to make the user unable to distinguish one setup from setup-cleanup-setup.

## Give every setup a cleanup

For a request tied to the mounted component, abort or ignore the obsolete result:

```tsx
useEffect(() => {
  const controller = new AbortController()
  let active = true

  void loadTranslations({ signal: controller.signal }).then(result => {
    if (active) translationStore.replace(result)
  })

  return () => {
    active = false
    controller.abort()
  }
}, [])
```

For a WebSocket, subscribe in setup and unsubscribe in cleanup. For a DOM listener, add and remove the same listener. For an animation, stop or reset it. Strict Mode is checking symmetry.

Aborting a fetch may still let the server see the first request. If duplicate reads are expensive, deduplicate them in a client-side cache or data-fetching layer. If the effect performs a write, the server needs an idempotency key; a component lifecycle is never an exactly-once delivery mechanism.

## Know when you do not need an effect

If the work does not synchronize with an external system, move it out of the effect. Derive render data during render. Start a purchase from the click handler that represents the purchase. Load route data in the framework's data layer where caching and request ownership are explicit.

A `useRef` guard can be appropriate for a narrow UI bookkeeping case, but it is not a general Strict Mode fix. It may skip the real setup after the development cleanup, and it does nothing for a genuine later remount. It also hides whether the effect can release its resources.

## Test remounting on purpose

Mount the component, navigate away, and return. Change the authenticated user or locale. Disconnect and reconnect the network. The preloader should not leak a stale response from the previous context, and a connection should not remain subscribed after unmount.

The duplicate translation request was low severity. The lesson is high leverage: an effect that only works when setup happens exactly once is relying on a promise React never made.
