React render strict mode vs non strict mode

Viewed 1009

function App() {
 const temp = [];

 useEffect(() => {
    console.log(temp);
    temp.push("new element")
 })
  return (
   <div>
     
     {console.log(temp)}
   </div>
  );
}

When I run the code in both <React.StrictMode> and non strict mode, i got the following,

without strict mode

with strict mode

I was expecting the first console log in non-strict mode to be an empty array,since useEffect is run after initial render, does non-strict mode skip the initial rendering phase, or is it some other reason that the first console log in non strict mode is not an empty array?

2 Answers

This is not related to strict or non-strict mode, but more to the timing that the console log buffer is flushed to the output. Running in strict mode is slower than non-strict mode because of the additional checks. Thus the view of console log output could be different.

Note that the console.log() output is not synchronous with code execution. Going by the sequence of execution, you should expect all the console log outputs to be an empty array. But that's not what you see because when you use console.log(temp), you are not seeing the value of the array at the moment it's logged.

If you use console.log(JSON.parse(JSON.stringify(temp))), you will see all console output an empty array.

You can refer to mozilla's recommendation on logging objects:

Don't use console.log(obj), use console.log(JSON.parse(JSON.stringify(obj))).

This way you are sure you are seeing the value of obj at the moment you log it. Otherwise, many browsers provide a live view that constantly updates as values change. This may not be what you want.

No, I think what you see is expected when using reacts StrictMode component.

Detecting unexpected side effects

React does work in two phases, a "render" phase and a "commit" phase.

  • The render phase determines what changes need to be made to e.g. the DOM. During this phase, React calls render and then compares the result to the previous render.
  • The commit phase is when React applies any changes. (In the case of React DOM, this is when React inserts, updates, and removes DOM nodes.) React also calls lifecycles like componentDidMount and componentDidUpdate during this phase.

React can invoke the render phase more than once prior to committing.

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:

  • Class component constructor, render, and shouldComponentUpdate methods
  • Class component static getDerivedStateFromProps method
  • Function component bodies
  • State updater functions (the first argument to setState)
  • Functions passed to useState, useMemo, or useReducer

The entire functional component body is the "render" function. The effect runs during the "commit" phase as a "lifecycle function".

Edit react-render-strict-mode-vs-non-strict-mode

This demo shows both at the same time.

  1. Notice there are several console logs from the with-StrictMode component, but only ever 2 with the without-StrictMode component.
  2. If you comment out the useEffect hook and re-run the sandbox, notice there is a double logging from the Strictmode wrapped app and only a single from the unwrapped one.

What you will see is that there will always be at least one (more if running in StrictMode) log from the "render" phase, and always exactly one log from the useEffect which runs once per render cycle, i.e. the "commit" phase.

What is likely also compounding the issue is the fact that you are mutating the array reference and it is likely mutated with the new value by the time the console log buffer is flushed to output. Remember, the second console log in the return is the equivalent to a side-effect in a "render" function which should be a pure function. With this side-effect there are to be unexpected results, which is of course the entire point of the StrictMode.

Related