简体   繁体   中英

React: this.state vs this.setState

Scenario 1:

const { foo } = this.state;

Scenario 2:

this.setState(({ foo }) => { ... });

is foo guaranteed to be identical between these two cases? Or will setState ever run async and return a different value based on other setState s?

State might be state when you access the state immediately after using this.setState , as this.setState is async .

If you need to compute on basis on the updated state ie ( after this.state is invoked ), you can use the 2nd argument for the method which is a callback that gets triggered after the state changes are committed.

this.setState(updateState, () => {
   const { foo } = this.state;

   console.log(foo); // updated foo
});

function updatedState({ foo }) { 
    // you can be sure that `foo`
    // is from the previous state 
    // before setState is called
}

As the React docs mention:

setState() does not always immediately update the component. It may batch or defer the update until later. This makes reading this.state right after calling setState() a potential pitfall. Instead, use componentDidUpdate or a setState callback (setState(updater, callback)), either of which are guaranteed to fire after the update has been applied.

So we can use the second argument of the setState to pass a callback where we execute our logic which depends on having the updated value of foo. However your initial question was whether the value of foo in const { foo } = this.state; and the value of foo in this.setState(({ foo }) => { ... }); was the same.

In order check this we can compare executing a setState followed by this.state.foo and a setState followed by another setState (the second one will just log the value of foo instead of mutating it). Please refer to the following snippet:

 class Example extends React.Component { constructor() { super(); this.state = { foo: 0, boo: 0 } } handleClickSetState = () => { this.setState(({foo}) => ({foo: foo+1})); this.setState(({foo}) => console.log("inside setState. foo: ", foo)); } handleClickState = () => { this.setState(({boo}) => ({boo: boo+1})); console.log("outside setState. boo: ", this.state.boo) } render() { return <React.Fragment> <button onClick={this.handleClickState}>Test state</button> <button onClick={this.handleClickSetState}>Test setState</button> </React.Fragment> } } ReactDOM.render(<Example />, document.getElementById("root")); 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script> <div id="root"></div> 

As we can see the "state case" lags behind by 1, which we would expect since setState is async (and we didn't use the second argument of setState ). However the "setState case" shows that the correct value is always shown, even if we don't utilize the second argument callback in setState.

In conclusion const { foo } = this.state; will always give you the immediate value of foo, regardless of pending state merges, while this.setState(({ foo }) => { ... }); seems like it will first finish pending updates before executing the callback, which means foo will always have the most recent value.

setState is async, and pulling values off state to use in setState is a potential source of bugs. setState can take multiple arguments. It can take just a new state, a callback that takes old state + props and returns new state, new state and a function to run after setting new state, or a combination. Example:

this.setState(
  // function taking previous state + props,
  // which should return updated state.
  // this could also just be an object with
  // the new state, if you don't need to rely
  // on previous state.
  (previousState, props) => {
    if (previousState.something) {
      return { something: somethingElse }
    } else {
      return { something: anotherThing }
    }
  }, () => {
    // safe to use new state here,
    // since state has been updated.
    // but it's recommended to use
    // componentDidUpdate instead!
  })

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM