Search code examples
javascriptreactjsreact-hooks

State is not updated in StrictMode


After some trial I discovered following problem occurs in strict mode. I would be interested if someone can explain why.

Take this simple example where inside render I am just scheduling a timeout which updates state:

Example:

let firstRender = true; // Normally I would use ref but I was playing with example

export default function App() {
  let [data, setData] = React.useState({ name: 'Nick' });

  // Schedule a timeout on first render
  if (firstRender) {
    setTimeout(() => {
      console.log('Running');

      setData((ps) => ({
        ...ps,
        name: 'Paul',
      }));
    }, 1000);
  }

  console.log('Running render');
  firstRender = false;

  return (
    <div>
      <h1>{data.name}</h1>
      <p>Start editing to see some magic happen :)</p>
    </div>
  );
}

If you run this example without Strict mode, then you will see "Paul" on screen after one second, as I was expecting.

If you use Strict mode, it will always show "Nick" on screen. Idea why?

Note: This behavior can't be repeated if you use useRef instead of the global variable firstRender even in Strict mode. This seems to happen because ref was set in first render, and its value got discarded (see also the answer).


Solution

  • This is due to the fact that strict mode intentionally invokes your function component body twice (when in dev mode) to help spot unintended side effects.

    On the second invocation, your firstRender variable is false so your setTimeout doesn't run.

    Important to note that this second invocation isn't just a re-render like you'd get from a state update. It's a second invocation of the entire component body. State is not preserved. React invokes your component function once, discards the result, and invokes it a second time to get the output.

    From the docs:

    Because the above methods might be called more than once, it’s important that they do not contain side-effects.

    Strict mode can’t automatically detect side effects for you, but it can help you spot them by making them a little more deterministic. This is done by intentionally double-invoking the following functions:

    • Function component bodies