在同步中执行setState,发现setState会异步执行。
在异步中执行setState,发现setState会同步执行。
setState第二个参数是个回调函数,state和DOM更新完后就会被触发
export default class App extends Component {
state = {
cnt: 1
}
render() {
return (
<div>
{this.state.cnt}
<button onClick={this.add1}>add1</button>
<button onClick={this.add2}>add2</button>
</div>
)
}
//同步逻辑中setState异步执行
add1 = () => {
this.setState({
cnt: this.state.cnt + 1
}, () => {
console.log(this.state.cnt)//2 3 4
})
this.setState({
cnt: this.state.cnt + 1
}, () => {
console.log(this.state.cnt)//2 3 4
})
}
//异步逻辑中setState同步执行
add2 = () => {
setTimeout(() => {
this.setState({
cnt: this.state.cnt + 1
}, () => {
console.log(this.state.cnt)//2 4 6
})
this.setState({
cnt: this.state.cnt + 1
}, () => {
console.log(this.state.cnt)//3 5 7
})
}, 0)
}
}
但是此特点在react18中已经被修复(被称为Automatic Batching,即自动批处理):不论是否处于异步逻辑中,多个state更新都会统一到一次render中渲染,以提升性能
如果不想采用这种批处理方案,可以通过flushSync来强制同步执行,直接更新一次DOM。
// 不兼容
handleClick = () => {
setTimeout(() => {
this.setState(({ count }) => ({ count: count + 1 }));
// 在 React17 及之前,打印出来是 { count: 1, flag: false }
// 在 React18,打印出来是 { count: 0, flag: false }
console.log(this.state);
this.setState(({ flag }) => ({ flag: !flag }));
});
};
// 更改后兼容
handleClick = () => {
setTimeout(() => {
ReactDOM.flushSync(() => {
this.setState(({ count }) => ({ count: count + 1 }));
});
// 在 React18,打印出来是 { count: 1, flag: false }
console.log(this.state);
this.setState(({ flag }) => ({ flag: !flag }));
});
};