课堂大纲
- 用法
- 原理
用法
1.参数是对象
this.setState({
counter: this.state.counter + 1,
})
2.参数是对象和callback
this.setState({
counter: this.state.counter + 1,
},() => {
// 可以在这拿到更新后的状态
console.log(this.state)
})
3.参数是函数(和callback)
this.setState((prevState) => {
// 参数是上一个状态 就是当前状态
// 返回一个对象 这个对象会合并到this.state中
return {
counter: this.state.counter + 1,
}
},() => {})
原理
执行this.setState其实就是执行this.updater.enqueueSetState
setState函数在Component.prototype上
Component.prototype.setState = function(partialState, callback) {
...
this.updater.enqueueSetState(this, partialState, callback, 'setState');
};
this.updater是react实例上的对象
function adoptClassInstance(workInProgress: Fiber, instance: any): void {
// 给react实例上添加updater对象
instance.updater = classComponentUpdater;
...
}
classComponentUpdater对象中包含enqueueSetState方法
const classComponentUpdater = {
enqueueSetState(inst, payload, callback) {
...
// 创建一个update 执行一个setState就会创建一个update
const update = createUpdate(expirationTime);
...
// 在enqueueUpdate函数里做了两件事
// 1.调用createUpdateQueue创建updateQueue
// updateQueue是单向链表 用next来串联update
// 2.调用appendUpdateToQueue将update放入updateQueue
enqueueUpdate(fiber, update);
// 进入update调度流程 updateQueue会被执行 新的状态会被更新到组件上
// 并开始后续的Virtual DOM更新,diff算法
scheduleWork(fiber, expirationTime);
},
enqueueReplaceState(inst, payload, callback) {
...
},
enqueueForceUpdate(inst, callback) {
...
},
};
为什么下面这种情况只会执行一次?
add3times = () => {
this.setState({counter: this.state.counter + 1})
this.setState({counter: this.state.counter + 1})
this.setState({counter: this.state.counter + 1})
}
这种写法会触发react batchUpdate机制,就是将一段时间内对state的修改批量更新到view,这就是batchUpdate机制。
实现原理(结合上面源码):
使用队列queue保存update,并在合适的时机处理queue。
什么时候执行queue?
react中是通过Transaction(事务)来实现的,Transaction对一个函数进行包裹,在函数执行前加入initialize阶段,然后函数执行阶段,最后close阶段。
在initialize阶段,当前线程的batchUpdate置为true,updateQueue被创建,当调用的this.setState时,此时状态不会被立即更新,而是创建一个update 并被push到updateQueue中,并且合并update。
函数执行结束调用事务的close阶段,updateQueue会被批处理。
怎样才能避开batchUpdate机制?
如果是 Ajax, setTimeout,addEventListener 等要离开当前线程进行异步操作的时候会脱离当前的事务,这时候再进入此次处理的时候batchUpdate=false就不会触发batchUpdate机制。
add3times = () => {
setTimeout(_ => {
this.setState({counter: this.state.counter + 1})
this.setState({counter: this.state.counter + 1})
this.setState({counter: this.state.counter + 1})
}, 0)
}