# React 18’s StrictEffectMode

These notes are copied from my Reddit comments(opens new window) about my earlier Notes about React 18 RC.

# About StrictEffectMode

Comment(opens new window)

One more tidbit that I found particular interesting [in React 18] is the “doubling” of effects in <React.StrictMode> (no.10). I got bitten by it. I used to write code like this:

function App() {
  const canvasRef = useRef(null)
  useEffect(() => {
    window.addEventListener('resize', () => {
      resizeCanvasToWindow(canvasRef.current)
    })
  }, [])
  // ...
}

Note that I never called removeEventListener(). Normally this is a red flag, but since it’s the root component that never gets unmounted, this is actually fine. At least, in earlier versions of React this is not problem (although they render twice(opens new window), they invoke effects just once.)

However, in React 18, under <React.StrictMode>, under development build, React will actually invoke the effect twice (i.e. effect()?.(); effect()) just to force you to always clean up after your effects.

With the example above, the event listener is now attached twice, so each 'resize' event invokes resizeCanvasToWindow() 2 times. (To fix, I need to add removeEventListener() in useEffect’s return. Took me a while to figure that one out!)

# The history of StrictEffectMode

Comment(opens new window)

In case you are curious about the story I git blame’d the test files(opens new window) and here’s what I found:

  • This feature is called StrictEffectsMode (test file name).
  • Sep 2020: The feature is implemented in PR #19523(opens new window) but is hidden behind a feature flag.
  • Feb 2021: 3 levels of strict modes introduced in PR #20844(opens new window) and PR #20849(opens new window).
    • Level 0 (sloppy mode) is ReactDOM.render.
    • Level 1 (legacy mode) is ReactDOM.render + StrictMode (which double-renders).
    • Level 2 (strict effects mode) is ReactDOM.createRoot + StrictMode (which double-renders + double-effects).