React: React.StrictMode causes setState to fire twice

Created on 18 May 2018  路  17Comments  路  Source: facebook/react

Do you want to request a feature or report a bug? Bug

What is the current behavior?
When wrapping a with React.StrictMode, setState is fired twice

If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem. Your bug will get fixed much faster if we can run your code and it doesn't have dependencies other than React. Paste the link to your JSFiddle (https://jsfiddle.net/Luktwrdm/) or CodeSandbox (https://codesandbox.io/s/new) example below:
Here is the jsFiddle
By clicking on a title and checking the console, you should see that the setState is called twice (its callback, however, is called only once).

What is the expected behavior?
Not sure, it might be related to #12094, then the behaviour might be intended. But when using the previous state to set the new one, then the component breaks if setState is fired twice (for instance, toggle components don't work).

Which versions of React, and which browser / OS are affected by this issue? Did this work in previous versions of React?
React 16.3

Most helpful comment

splice mutates the array so this looks like the type of impurity that strict mode is intended to catch :smile:

All 17 comments

The prevState => nextState callback is fired twice, but I don't see the behavior you're talking about with toggle components not working. If you add some more logging or rendering to your example, you'll see that the toggle in it does in fact work correctly:

https://jsfiddle.net/oq58bf9k/

It is expected that setState updaters will run twice in strict mode in development. This helps ensure the code doesn't rely on them running a single time (which wouldn't be the case if an async render was aborted and alter restarted). If your setState updaters are pure functions (as they should be) then this shouldn't affect the logic of your application.

@gaearon Consider this example

this.setState(({counter}) => {
  return { counter: counter + 1 };
});

say this piece of code will be triggered by clicking on a button. If setState updater function can be called multiple times under StrictMode, then how do I guarantee that my counter only increments by 1 per click?

Actually, this is a real example in my project, where I have a button, and setState was triggered after I clicked on the button. I noticed that the updater function in my setState was called twice, resulting in inconsistency.

@franklixuefei the updater should be called twice with the same state. For example, if counter is 0 it will be called with 0 twice, returning 1 in both cases.

Also I believe only one of the invocations actually cares about the value returned. So React isn't processing each state update twice, it's just calling the function twice to help surface issues related to side effects in a state updater and other methods that should be pure.

@aweary Thanks for answering my question! However, this is not the case. Take a look at the below real-life example from my project:

  private handleItemRemove(index: number) {
    const { onItemsChange } = this.props;
    this.setState(
      ({ selectedItems }) => {
        console.log('updater function called', selectedItems);
        // remove the item at index
        if (selectedItems.length > 0) {
        // remove one item at the index
          selectedItems.splice(index, 1);
        }
        return { selectedItems: [...selectedItems] };
      },
      () => {
        // tslint:disable-next-line:no-unused-expression
        this.inputNode && this.inputNode.focus();
        // tslint:disable-next-line:no-unused-expression
        onItemsChange && onItemsChange(this.state.selectedItems);
      }
    );
  }

where selectedItems is just an array of objects. I noticed that in this example, the updater function was called twice (can be seen from the console log), and the second time the updater function was called, selectedItems contains one fewer element than the first time. Trust me, I was banging my head on this bug last night...

splice mutates the array so this looks like the type of impurity that strict mode is intended to catch :smile:

@iamdustan I noticed that too, but if you look closely, I created a new array at the end:

return { selectedItems: [...selectedItems] };

Oh wait... You are right! It modified the original array even though I returned a new array!

I'm so surprised that I didn't even think of it at first. What a shame.

Now I realized how great React.StrictMode is.

Thanks @iamdustan and @aweary

Similarly, constructor() is called twice with the same props. If you're dispatching a loading action that triggers API fetching and more action dispatching via something like saga then it makes development pretty confusing. You might see flashes of UI changes as the component enters its loading state multiple times.

I'm not sure how helpful this is for day to day development. I guess it's just a downside to relying on side effects with Saga?

I am experiencing the same "issue" (setState updater function called twice) even if I do not use React.StrictMode (I am using React 16.8.6). Is this behaviour enabled by default on React 16.8.6?

Surely execution of setState (and its count of execution) is a functional req and should be identical across development and production modes?

Only difference between dev and prod should be data or non-functional reqs IMO.

The whole purpose of StrictMode is to find cases where the user-written functions that are supposed to be pure are, in fact, not pure. If they are pure, then there is no observable difference between development and production modes.

f they are not pure, Strict Mode helps find that issue. This is to prepare for the future, where React doesn't give any guarantees about the number of times they would be invoked, even in production. You can opt out of Strict Mode if you don't want this behavior, but it is the very purpose of Strict Mode.

Executing twice to detect an intermittent error in a potentially 'unpure' function is arbitrary. They could argue you need 10 executions so I'm struggling to see why this is a justification for Strict Mode at the package level, when it should and could be a user-defined mode.

Executing twice to detect an intermittent error in a potentially 'unpure' function is arbitrary.

Not an error, but a mutation or a side effect. Yes, it is arbitrary, but we've found it works really well in practice. In fact, you can see an example of this right above in the comments: https://github.com/facebook/react/issues/12856#issuecomment-393643981. A "proper" solution to this would involve having some way to track effects on the language level. There are languages that do this (e.g. Koka) so it's conceivable that eventually JS type systems might be able to express some version of this. In the meantime, we use what works in practice.

They could argue you need 10 executions so I'm struggling to see why this is a justification for Strict Mode at the package level, when it should and could be a user-defined mode.

I'm not sure what you mean by either "justification at the package level" or a "user-defined mode". Strict Mode is a part of React precisely because user code has no control over how React calls its functions. So it's React that needs to execute these functions twice (if you can get onboard with this heuristic). You're welcome to remove StrictMode from your app if that confuses you.

The whole purpose of StrictMode is to find cases where the user-written functions that are supposed to be pure are, in fact, not pure. If they are pure, then there is no observable difference between development and production modes.

f they are not pure, Strict Mode helps find that issue. This is to prepare for the future, where React doesn't give any guarantees about the number of times they would be invoked, even in production. You can opt out of Strict Mode if you don't want this behavior, but it _is_ the very purpose of Strict Mode.

@gaearon Is Strict Mode enabled by default as of React 16.13.1?

Thanks

Same issue here.

handleChange(id){
    this.setState(state => {
      const newtodos = state.todos.map(todo => {
        if(todo.id === id){
          todo.completed = !todo.completed
        }
        return todo
      })
      return {todos: newtodos}
    })
  }

The handleChange function is called for handling the checkbox onChange event. Due to strict mode, it doesn't change the value of the completed. Because it is called twice it goes back to the previous state. I am using the completed to check or uncheck the checkbox. So is this not a pure function?

@akhileshkcoder In your case, you are mutating the nth todo when todo.id === id, so, even though newtodos is a new array when you return the next state at return {todos: newtodos}, your setState callback function is not pure.

Try this instead:

handleChange(id){
    this.setState(state => {
      const newtodos = state.todos.map(todo => {
        if(todo.id === id){
          return {
              ...todo,
              completed: !todo.completed
          };
        }
        return todo
      })
      return {todos: newtodos}
    })
  }


This way the setState's callback is idempotent and even if React calls it more than once, it will lead to the correct state.

@tonix-tuft Cool!. It worked. Thank you.

Was this page helpful?
0 / 5 - 0 ratings