1.)setState操作是异步的,并且为了性能提升而进行批处理。这在setState的文档中有解释。
setState()不会立即改变this.state,但会创建挂起状态转换。调用此方法后访问this.state可能会返回现有值。无法保证对setState的调用进行同步操作,并且可以对调用进行批处理以提高性能。
2.)为什么它们会使setState异步,因为JS是单线程语言,并且此setState不是WebAPI或服务器调用:
这是因为setState改变了状态并导致重新渲染。这可能是一项昂贵的操作,并使其同步可能会使浏览器无响应。
因此,setState调用是异步的以及批处理以获得更好的UI体验和性能。
setState
意图是异步的,在Dan Abramov的链接解释中有一些非常好的理由。这并不意味着它总是异步的 - 它主要意味着你不能依赖它是同步的。ReactJS考虑了您正在更改状态的场景中的许多变量,以确定何时
state
应该实际更新并重新呈现组件。
一个简单的例子来说明,如果你打电话
setState
作为对用户操作的反应,那么
state
可能会立即更新(虽然,你再也不能指望它),所以用户不会感到任何延迟,但如果你打电话
setState
为了响应ajax调用响应或其他一些不是由用户触发的事件,那么状态可能会稍微延迟更新,因为用户不会真正感受到这种延迟,并且它会通过等待来提高性能批处理多个状态一起更新并重新渲染DOM的次数更少
想象一下在某个组件中递增计数器:
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>
...
)
}
}
有一个计数处理程序附加到父组件和子组件。这是故意完成的,因此我们可以在相同的单击事件冒泡上下文中执行两次setState(),但是从2个不同的处理程序中执行。
正如我们想象的那样,按钮上的单击事件现在将触发这两个处理程序,因为事件在冒泡阶段从目标气泡到最外面的容器。
因此,btnCountHandler()首先执行,期望将计数增加到1然后执行divCountHandler(),期望将计数增加到2。
但是,计数只会增加到1,因为您可以在React Developer工具中进行检查。
这证明了反应
- 排队所有setState调用
- 在上下文中执行最后一个方法后回到此队列(在本例中为divCountHandler)
- 将在同一上下文中的多个setState调用内发生的所有对象变换(例如,单个事件阶段中的所有方法调用都是相同的上下文)合并为一个单个对象变异语法(合并是有意义的,因为这就是我们可以独立更新状态属性的原因在setState()中首先)
- 并将其传递给一个单独的setState()以防止由于多个setState()调用而重新呈现(这是一个非常原始的批处理描述)。
反应运行的结果代码:
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');
}