想象一下在某些组件中增加一个计数器:
class SomeComponent extends Component{
state = {
updatedByDiv: '',
updatedByBtn: '',
counter: 0
}
divCountHandler = () => {
this.setState({
updatedByDiv: 'Div',
counter: this.state.counter + 1
});
console.log('divCountHandler executed');
}
btnCountHandler = () => {
this.setState({
updatedByBtn: 'Button',
counter: this.state.counter + 1
});
console.log('btnCountHandler executed');
}
...
...
render(){
return (
...
// a parent div
<div onClick={this.divCountHandler}>
// a child button
<button onClick={this.btnCountHandler}>Increment Count</button>
</div>
...
)
}
}
父组件和子组件都有一个计数处理程序。这样做是有目的的,因此我们可以在相同的click事件冒泡上下文中执行setState()两次,但是可以在2个不同的处理程序中执行两次。
就像我们想象的那样,按钮上的一次单击事件现在将触发这两个处理程序,因为该事件在冒泡阶段从目标冒泡到最外层容器。
因此,btnCountHandler()首先执行,期望将计数增加到1,然后divCountHandler()执行,期望将计数增加到2。
但是,正如您可以在React Developer工具中检查的那样,该计数仅增加到1。
这证明了反应
将所有setState调用排队
在执行上下文中的最后一个方法后返回此队列(本例中为divCountHandler)
将在同一上下文中发生在多个setState调用中的所有对象突变(例如,单个事件阶段中的所有方法调用都是相同的上下文)合并为一个对象突变语法(合并是有道理的,因为这就是为什么我们可以独立更新状态属性的原因)首先在setState()中)
并将其传递到单个setState()中,以防止由于多个setState()调用而导致重新呈现(这是批处理的非常原始的描述)。
结果代码由react运行:
this.setState({
updatedByDiv: 'Div',
updatedByBtn: 'Button',
counter: this.state.counter + 1
})
要停止此行为,将传递回调,而不是将对象作为参数传递给setState方法。
divCountHandler = () => {
this.setState((prevState, props) => {
return {
updatedByDiv: 'Div',
counter: prevState.counter + 1
};
});
console.log('divCountHandler executed');
}
btnCountHandler = () => {
this.setState((prevState, props) => {
return {
updatedByBtn: 'Button',
counter: prevState.counter + 1
};
});
console.log('btnCountHandler executed');
}
在最后一个方法完成执行之后,当react返回以处理setState队列时,它仅对排队的每个setState调用回调,并传入先前的组件状态。
这种反应方式可确保队列中的最后一个回调能够更新其所有先前对应对象已动手的状态。