简体   繁体   中英

React setState Not Waiting Even With Proper Format

This is the recommended way of calling setState in React:

this.setState((state, props) => ({
  counter: state.counter + props.increment
}));

However, this format still does not wait for setState to complete before going to the next line in this code:

  pressed = () => {
    this.setState((prevState, props) => ({
      token: 1
    }), () => console.warn("TOKEN setState Callback: " + this.state.token));
    console.warn("TOKEN before timeout: " + this.state.token);
    setTimeout(function() {console.warn("TOKEN after timeout: " + this.state.token)}.bind(this), 500);
    ...
    //rest of function
  }

In this case, this is the order of the output
1. TOKEN setState Callback: 1
2. TOKEN before timeout: 0
3. TOKEN after timeout: 0 .

I don't want to put the //rest of my function in the callback because it's a pretty long function. Making the function "async" and prepending this.setState with await works, but is not recommended on various sources.

Why doesn't my code work? How can I make the setState call finish before calling TOKEN before timeout, without largely changing my code?

Thanks.

Providing a callback function doesn't cause setState to become synchronous. Rather, you're supposed to put your code in the callback function, and it will be called once the set state is done.

    this.setState((prevState, props) => ({
      token: 1
    }), () => {
      console.warn("TOKEN setState Callback: " + this.state.token);
      console.warn("TOKEN before timeout: " + this.state.token);
      setTimeout(function() {console.warn("TOKEN after timeout: " + this.state.token)}.bind(this), 500);
      //rest of function
    });

I don't want to put the //rest of my function in the callback because it's a pretty long function.

If you want the code to run after the set state, then put it in the callback. You could extract it to a helper method if that makes it easier to read, but the length of your code doesn't change where you need to call it.

Your other option is to put the code in componentDidUpdate, and wrap it in a check to see if the token changed:

componentDidUpdate(prevProps, prevState) {
  if (prevState.token !== this.state.token) {
      console.warn("TOKEN before timeout: " + this.state.token);
      setTimeout(function() {console.warn("TOKEN after timeout: " + this.state.token)}.bind(this), 500);
      //rest of function
  }
}

Making the function "async" and prepending this.setState with await works, but is not recommended on various sources.

The reason that's not recommended is it's relying on an implementation detail of react. They happen to implement the batching of setState in such a way that if you queue up a microtask, you'll wait long enough for react to finish its work. But react does not guarantee that this is the way they will implement it.

From the documentation

Think of setState() as a request rather than an immediate command to update the component. For better perceived performance, React may delay it, and then update several components in a single pass. React does not guarantee that the state changes are applied immediately.

So even though you have ran setState, it is not going to automatically update the token for you to use the new value within your function. If the token is only used inside of that token, then I would set a variable inside to increment the token and use it exclusively inside of your function, and then at the end set your state with this.setState({token: 1})

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