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.