4
 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?

6
  • What version of react are you using? I can't reproduce this. Commented Oct 21, 2020 at 6:27
  • @SamR. version 16.14.0 Commented Oct 21, 2020 at 6:28
  • It's strange or I'm doing something stupid but temp gets redeclared on every render so it only logs and empty array for me. Not sure how you see the new element pushed. Commented Oct 21, 2020 at 6:29
  • useEffect runs after the render, that's why it should print an empty array every time. Commented Oct 21, 2020 at 6:31
  • I think it might be related to console.log ? since useEffect in this case isn't altering the state, shouldn't re render not even trigger in the first place? Commented Oct 21, 2020 at 6:52

2 Answers 2

2

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.

Sign up to request clarification or add additional context in comments.

Comments

1

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.

4 Comments

I ran your example on stackblitz and looked at the console they have and it still prints and empty array. Then I looked at the actual browser console and now I see the element. This is the correct answer.
Still boggles my mind as to why the devtool shows that as an empty array but when you expand it, there is an element in there with length 1. See gfycat.com/faroffwanfairyfly
@SamR. Yes, this was the last little bit of my answer which briefly says that the console logging ins't lock-step with the code execution and the mutation populates the array after the log was scheduled but before it is flushed to the output (console/terminal/etc..). It certainly causes a lot of confusion when coupled with the inexperience of the react component lifecycle.
@DrewReese From the doc it mentioned "The commit phase is usually very fast, but rendering can be slow.". Since ``` {console.log(temp)} ``` is in the rendering phase, this line of code hasn't been run but the commit phase has already completed, by the time this line is run, ``` temp ``` has been mutated due to the commit phase, that's why it won't print an empty array. Am I understanding it correctly?

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.