Skip to content

React.StrictMode causes setState to fire twice #12856

Closed
@larabr

Description

@larabr

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

Activity

nmain

nmain commented on May 18, 2018

@nmain

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/

gaearon

gaearon commented on May 18, 2018

@gaearon
Collaborator

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.

franklixuefei

franklixuefei commented on May 31, 2018

@franklixuefei

@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.

aweary

aweary commented on May 31, 2018

@aweary
Contributor

@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.

franklixuefei

franklixuefei commented on May 31, 2018

@franklixuefei

@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...

iamdustan

iamdustan commented on May 31, 2018

@iamdustan
Contributor

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

franklixuefei

franklixuefei commented on May 31, 2018

@franklixuefei

@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

kumar303

kumar303 commented on Sep 26, 2018

@kumar303

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?

tonix-tuft

tonix-tuft commented on Aug 3, 2019

@tonix-tuft

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?

34 remaining items

Loading
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @kumar303@iamdustan@gaearon@franklixuefei@aweary

        Issue actions

          React.StrictMode causes setState to fire twice · Issue #12856 · facebook/react