引子:先来提神醒脑一下:
class Example extends React.Component {
constructor() {
super();
this.state = {
secretNumber: 10
};
}
componentDidMount() {
this.setState({secretNumber: this.state.secretNumber + 1});
console.log(this.state.secretNumber); // 第 1 次 log
this.setState({secretNumber: this.state.secretNumber + 1});
console.log(this.state.secretNumber); // 第 2 次 log
setTimeout(() => {
this.setState({secretNumber: this.state.secretNumber + 1});
console.log(this.state.secretNumber); // 第 3 次 log
this.setState({secretNumber: this.state.secretNumber + 1});
console.log(this.state.secretNumber); // 第 4 次 log
}, 0);
}
render() {
return null;
}
};
快写下你的答案,十行后公布答案~~~
答案(10 10 12 13)
OK 以下是知识点总结
setState的异步性
- 调用
setState
其实是异步的 —— 不要指望在调用setState
之后,this.state
会立即映射为新的值。 - 将
setState()
视为请求而不是立即更新组件的命令。 -
setState()
并不总是立即更新组件。它会批量推迟更新。这使得在调用 setState() 后立即读取this.state
成为了隐患。
典型示例:
incrementCount() {
// 注意:这样 *不会* 像预期的那样工作。
this.setState({count: this.state.count + 1});
}
handleSomething() {
// 假设 `this.state.count` 从 0 开始。
this.incrementCount();
this.incrementCount();
this.incrementCount();
// 当 React 重新渲染该组件时,`this.state.count` 会变为 1,而不是你期望的 3。
// 这是因为上面的 `incrementCount()` 函数是从 `this.state.count` 中读取数据的,
// 但是 React 不会更新 `this.state.count`,直到该组件被重新渲染。
// 所以最终 `incrementCount()` 每次读取 `this.state.count` 的值都是 0,并将它设为 1。
// 问题的修复参见下面的说明。
}
如果你需要基于当前的 state
来计算出新的值,该怎么办?
incrementCount() {
this.setState((state) => {
// 重要:在更新的时候读取 `state`,而不是 `this.state`。
return {count: state.count + 1}
});
}
handleSomething() {
// 假设 `this.state.count` 从 0 开始。
this.incrementCount();
this.incrementCount();
this.incrementCount();
// 如果你现在在这里读取 `this.state.count`,它还是会为 0。
// 但是,当 React 重新渲染该组件时,它会变为 3。
}
-
setState()
的第二个参数为可选的回调函数,它将在setState
完成合并并重新渲染组件后执行。通常,我们建议使用componentDidUpdate()
来代替此方式。
总结: 调用 setState()
后立即读取 this.state
,更新依赖于当前的 state
的 state
需要特别注意。需要采取updater()
函数,回调函数或者 componentDidUpdate()
方案处理。
setState 什么时候是异步的
- 目前,在事件处理函数内部的 setState 是异步的。
如果 Parent 和 Child 在同一个 click 事件中都调用了 setState ,这样就可以确保 Child 不会被重新渲染两次。取而代之的是,React 会将该 state “冲洗” 到浏览器事件结束的时候,再统一地进行更新。这种机制可以在大型应用中得到很好的性能提升。
- 除此之外的setState调用会同步执行
this.state
所谓“除此之外”,指的是绕过React通过addEventListener
直接添加的事件处理函数,还有通过setTimeout/setInterval
产生的异步调用。
原因:在React的setState
函数实现中,会根据一个变量isBatchingUpdates
判断是直接更新this.state
还是放到队列中回头再说,而isBatchingUpdates
默认是false
,也就表示setState
会同步更新this.state
,但是,有一个函数batchedUpdates
,这个函数会把isBatchingUpdates
修改为true
,而当React在调用事件处理函数之前就会调用这个batchedUpdates
,造成的后果,就是由React控制的事件处理过程setState
不会同步更新this.state
。
setState
简化调用栈 图例
总结:在React中,如果是由React引发的事件处理(比如通过onClick引发的事件处理),调用setState不会同步更新this.state,除此之外的setState调用会同步执行this.state。所谓“除此之外”,指的是绕过React通过addEventListener直接添加的事件处理函数,还有通过setTimeout/setInterval产生的异步调用。
相信看到这里,大家已经明白为何答案为10 10 12 13 。
网友评论