Answer first: sometimes synchronous, sometimes asynchronous. SetState is asynchronous in synthesized events and lifecycle functions, and synchronous in native events and setTimeout
Synthetic events and lifecycle functions are asynchronous
We can look at one 🌰 :
export default class App extends React.Component {
constructor() {
super();
this.state = {
count: 0,
};
}
render() {
const { count } = this.state;
return (
<div>
<h1>{count}</h1>
<button id="add" onClick={this.btnChange}>+</button>
</div>
);
}
btnChange = () => {
this.setState({
count: this.state.count + 1,
});
console.log("this.state.count :>> ", this.state.count);
};
}
Copy the code
Now, when we click on the button, the printed state is 0, not the latest one,
setState
setState
callback
BtnChange = () => {this.setState({count: this.state.count + 1,}, () => {// Now print 1 console.log()"this.state.count :>> ", this.state.count); }); // Print 0 console.log("this.state.count :>> ", this.state.count);
};
Copy the code
It is important to note that in the console, 0 is printed first, then 1.
The same is true in the component lifecycle.
Second, in the native event andsetTimeout
It’s in sync
In native DOM times and setTimeout, this is synchronized, and we’ll modify the click events above
btnChange = () => {
setTimeout(() => {
this.setState({
count: this.state.count + 1,
});
console.log("this.state.count :>> ", this.state.count);
}, 0);
};
Copy the code
In this case, the updated count value is printed
DOM
componentDidMount() {
document.querySelector("#add").addEventListener("click", () => {
this.setState({
count: this.state.count + 1,
});
console.log("this.state.count :>> ", this.state.count);
});
}
Copy the code
Three, asynchronoussetState
Problems that may be merged
Also, there is a problem with asynchronous setState. When the same asynchronous setState operation is performed multiple times, it will be merged before updating.
btnChange = () => {
this.setState({
count: this.state.count + 1,
},() => {
console.log("this.state.count :>> ", this.state.count);
});
this.setState({
count: this.state.count + 1,
},() => {
console.log("this.state.count :>> ", this.state.count);
});
this.setState({
count: this.state.count + 1,
},() => {
console.log("this.state.count :>> ", this.state.count);
});
this.setState({
count: this.state.count + 1,
},() => {
console.log("this.state.count :>> ", this.state.count);
});
};
Copy the code
And the answer is obviously 1, 4 times
setState
setState
state
state
setState
state
state
count
state
state
state
So how do we keep it from merging?
We can return an object in setState in the form of a callback function:
btnChange = () => {
this.setState((state,props) => {
return {
count: state.count + 1,
};
},() => {
console.log('this.state.count :>> ', this.state.count);
});
this.setState((state,props) => {
return {
count: state.count + 1,
};
},() => {
console.log('this.state.count :>> ', this.state.count);
});
this.setState((state,props) => {
return {
count: state.count + 1,
};
},() => {
console.log('this.state.count :>> ', this.state.count);
});
this.setState((state,props) => {
return {
count: state.count + 1,
};
},() => {
console.log('this.state.count :>> ', this.state.count);
});
};
Copy the code
This callback takes two arguments, one is the current state and the other is props. On return {}, count is returned to state, setState is executed, and so on. When the asynchronous operation is complete, The callback function in setState is executed, at which point count is already the latest 4, so 4 is printed four times. Although we can’t see the result of its printing from 1 to 4, we need to be clear about this process.
In synchronous setState, no merge takes place
btnChange = () => {
setTimeout(() => {
this.setState({
count: this.state.count + 1,
},() => {
console.log("this.state.count :>> ", this.state.count);
});
});
setTimeout(() => {
this.setState({
count: this.state.count + 1,
},() => {
console.log("this.state.count :>> ", this.state.count);
});
});
setTimeout(() => {
this.setState({
count: this.state.count + 1,
},() => {
console.log("this.state.count :>> ", this.state.count);
});
});
setTimeout(() => {
this.setState({
count: this.state.count + 1,
},() => {
console.log("this.state.count :>> ", this.state.count);
});
});
};
Copy the code
We can see that the printed results are as expected:
Four, conclusion
Finally, I will talk about the reason why React does not allow direct modification of state. Remember PureComponent and shouldComponentUpdate, which are often used for performance optimizations. Its purpose is to intercept component rendering. Shallow comparison of the previous state and props to the updated state and props to determine whether to render the component. If we change the value of state directly, the comparison is meaningless and remains the same, and there is no way to optimize performance. Not to mention React won’t re-render at all if the value of state stays the same.
Because the author is still a front end small sprout new, unavoidably can have careless mistake, welcome big guys to give advice!