class Demo extends PureComponent {
state={
count: 0,
}
componentDidMount() {
console.log('pre state', this.state.count); // pre state 0
this.setState({
count: this.state.count + 1
});
console.log('next state', this.state.count); // next state 0
//测试setTimeout
setTimeout(() => {
console.log('setTimeout pre state', this.state.count); // setTimeout pre state 1
this.setState({
count: this.state.count + 1
});
console.log('setTimeout next state', this.state.count); // setTimeout next state 2
}, 0);
}
onClick = (event) => {
// 测试合成函数中setState
console.log(`${event.type} pre state`, this.state.count); // click pre state 2
this.setState({
count: this.state.count + 1
});
console.log(`${event.type} next state`, this.state.count); // click next state 2
}
render() {
return <button onClick={this.onClick}>count+1</button>
}
}
这里有三种方法调用 setState:
- 在 componentDidMount 中直接调用 setState;
- 在 componentDidMount 的 setTimeout 方法里调用 setState;
- 在 dom 中绑定 onClick 直接调用 setState;
在 React 中,如果是由 React 引发的事件处理(比如通过 onClick 引发的事件处理),调用 setState 不会同步更新 this.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 在生命周期函数和合成函数中都是异步更新。
- setState 在 setTimeout、原生事件和 async 函数中都是同步更新。每次更新不代表都会触发 render,如果 render 内容与 newState 有关联,则会触发,否则即便 setState 多次也不会 render。
- 如果 newState 内容与 render 有依赖关系,就不建议同步更新,因为每次 render 都会完整的执行一次批量更新流程(只是 dirtyComponets 长度为1,stateQueue 也只有该组件的 newState),调用一次 diff 算法,这样会影响 React 性能。
- 如果没有必须同步渲染的理由,不建议使用同步,会影响 react 渲染性能。
网友评论