legacy 模式 下的 setState
legacy 模式下:也就是 通过 ReactDom.render() 方法来挂载时,setState 在 生命周期函数,事件函数当中是批量异步更新,而在定时器 setTimeout | setInterval, Promsie, addEventListener…… 当中是 同步非批量更新。
如果想要在 legacy 模式下的定时器当中获得批量异步更新,通过使用 React 提供的 batchedUpdates 将更新包裹起来就可以强制获得异步批量更新。而事实上在生命周期,事件函数当中,源码默认包裹了 batchedUpdates 所以生命周期,事件函数当中是异步批量更新。
batheUpdate 原理
export function batchedUpdates<A, R>(fn: A => R, a: A): R {
const prevExecutionContext = executionContext;
executionContext |= BatchedContext;
try {
return fn(a);
} finally {
executionContext = prevExecutionContext;
// If there were legacy sync updates, flush them at the end of the outer
// most batchedUpdates-like method.
if (
executionContext === NoContext &&
// Treat `act` as if it's inside `batchedUpdates`, even in legacy mode.
!(__DEV__ && ReactCurrentActQueue.isBatchingLegacy)
) {
resetRenderTimer();
flushSyncCallbacksOnlyInLegacyMode();
}
}
}
batheUpdate 原理:将模式暂时 noMode 改成了 conCurrent ,然后再去执行点击事件,fiber 节点调度到 scheduleFiberOnRoot 就不会被判断 是 noMode ,所以不会同步执行,而是 通过 performConcurrentWorkOnRoot 批量异步更新。
如果是 setTimeout 没有包裹 batheUpdate 的话,判断是 noMode 就会立即同步执行 setTimeout 中的 setState , 通过 performSyncWorkOnRoot 进行同步更新。
异步批量更新
import * as React from 'react';
import * as ReactDOM from 'react-dom';
class Counter extends React.Component{
state = {number:0}
buttonClick = ()=>{
console.log('buttonClick');
this.setState({number:this.state.number+1});
console.log(this.state.number);
this.setState({number:this.state.number+1});
console.log(this.state.number);
}
divClick = ()=>{
console.log('divClick');
}
render(){
return (
<div onClick={this.divClick} id="counter">
<p>{this.state.number}</p>
<button onClick={this.buttonClick}>+</button>
</div>
)
}
}
ReactDOM.render(<Counter/>,document.getElementById('root'));
同步非批量
import * as React from 'react';
import * as ReactDOM from 'react-dom';
class Counter extends React.Component{
state = {number:0}
setTimeout(()=>{
this.setState((state)=>({number:state.number+1}),()=>{
console.log(this.state.number);
});
this.setState((state)=>({number:state.number+1}),()=>{
console.log(this.state.number);
});
});
}
render(){
return (
<div onClick={this.divClick} id="counter">
<p>{this.state.number}</p>
<button onClick={this.buttonClick}>+</button>
</div>
)
}
}
ReactDOM.render(<Counter/>,document.getElementById('root'));
concurrent 模式 下的 setState
concurrent模式下:也就是通过 ReactDom.creatRoot().render() 方法来挂载时,这里的所有更新 - 生命周期函数,事件函数,定时器 setTimeout | setInterval, Promsie, addEventListener…… 都是异步批量更新。
挂载时,不多说,以异步优先级去调度,异步并发更新
更新时,Hook 更新,不多说,大家可以看看之前的文章。
下面我们来聊聊类组件的更新。
第一:由于组件类 extends Components | PureComponents
PureComponents 本质就是(使用 shalowEquall 重写 shouldUpdate方法)
第二:调用类组件的 setState 方法 ,在setState 方法中调用 this.updater.enqueuSetState 方法。
第三:通过实例的 reactInternals 方法找到 对应的 fiber, createUpdate 创建更新对象 update
第四:过 enqueueUpdate将 更新对象 update , push 到 fiber.updateQueue 当中。开始scheduleUpdateOnFiber。
第五:异步调度到该类组件的 fiber 时,会通过 processUpdateQueue 函数, 更新 state。
processUpdateQueue 函数可以让 React 获得高优先级打断低优先级更新的能力。
第六: 提交,Dom 更新