You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
function Component(props, context, updater) {
this.props = props;
this.context = context;
// If a component has string refs, we will assign a different object later.
this.refs = emptyObject;
// We initialize the default updater but the real one gets injected by the
// renderer.
this.updater = updater || ReactNoopUpdateQueue;
}
Component.prototype.isReactComponent = {};
...
Component.prototype.setState = function(partialState, callback) {
invariant(
typeof partialState === 'object' ||
typeof partialState === 'function' ||
partialState == null,
'setState(...): takes an object of state variables to update or a ' +
'function which returns an object of state variables.',
);
this.updater.enqueueSetState(this, partialState, callback, 'setState');
};
function adoptClassInstance(workInProgress: Fiber, instance: any): void {
instance.updater = classComponentUpdater;
workInProgress.stateNode = instance;
// The instance needs access to the fiber so that it can schedule updates
setInstance(instance, workInProgress);
if (__DEV__) {
instance._reactInternalInstance = fakeInternalInstance;
}
}
function performWork(minExpirationTime, isYieldy) {
// Keep working on roots until there's no more work, or until there's a higher
// priority event.
findHighestPriorityRoot();
if (isYieldy) {
...
} else {
while (nextFlushedRoot !== null && nextFlushedExpirationTime !== NoWork && minExpirationTime <= nextFlushedExpirationTime) {
performWorkOnRoot(nextFlushedRoot, nextFlushedExpirationTime, false);
findHighestPriorityRoot();
}
}
...
finishRendering();
}
React 源码解析: setState
这一节探讨 setState。
本节 demo 如下:
setState 定义
组件是通过 React.Component 继承而来的,React.Component 定义在 ReactBaseClasses.js 文件中:
所以 setState 是通过继承而来的。在 setState 函数中调用 this.updater.enqueueSetState 函数,其中 this.updater 是实例化 Component 时初始化的。
以开始 demo 为例,App(Component)组件是经过以下步骤初始化的:
ctor 就是 App,所以 constructor() 实例 Component 时,参数为空。this.updater = ReactNoopUpdateQueue,但是 ReactNoopUpdateQueue 此时为空。其实在 constructClassInstance 函数中,在 new ctor(props, context) 之后,还调用了一个函数 adoptClassInstance:
在该函数中 instance.updater = classComponentUpdater 对 updater 进行赋值。
在 classComponentUpdater 对象中存在 enqueueSetState 函数。现在理清了 setState 代码。enqueueSetState 函数就是实际执行 setState 函数的地方。
setState 批量更新
我们知道 setState 有批量更新,比如:
我们点击按钮时,div 内容变为 1,也就是说 this.setState 只有最后一个起作用。
调用 setState 函数,最终会进入 enqueueSetState 函数,在 enqueueSetState 中会调用 enqueueUpdate(fiber, update)。enqueueUpdate 函数的作用是:创建 updateQueue 队列,通过 firstUpdate、lastUpdate、lastUpdate.next 去维护一个更新的队列。最后会在 performWork 中,相同的 key 会被覆盖,只会对最后一次的 setState 进行更新。
所以在 clickButton 函数中,前两个 setState 是无效的。
enqueueSetState 函数最后会调用 scheduleWork 更新。如果每次 setState 都让 scheduleWork 更新完成,那么每次 setState 都应该有效的,这和批量更新时矛盾的。React 针对这种情况作了处理:分为合成事件的 setState 和生命周期的 setState。
在合成事件中我们知道会把 isBatchingUpdates 置为 true,具体是在函数 interactiveUpdates$1 中:
当调用 setState 进入到 requestWork 函数时,因为 isBatchingUpdates 为 true 而直接返回。在 interactiveUpdates$1 函数中 try finally 语句保证了执行完事件函数后,最后都要执行 performSyncWork 方法将变动部分重新渲染。
在 commit 流程章节中,已经介绍生命周期函数会在 commitRoot 中执行。
当执行 componentDidMount 时会调用 setState 函数,回到 enqueueSetState 函数中。在进入 requestWork函数时,因为 isRendering 为 true,直接返回。和合成事件中的 setState 一样,生命周期函数中的 setState 到最后肯定都要重新渲染。保证重新渲染地方是在 performWork 函数中的 while 语句:
当执行完 commitRoot 函数后,会逐层返回到 performWork 函数中,此时 isRendering 已经重置为 false。然后调用 findHighestPriorityRoot 函数,在 findHighestPriorityRoot 函数作用是找出优先级最高的 root。
上面的例子会进入:
所以最后 nextFlushedRoot、nextFlushedExpirationTime 都有值,因此
while (nextFlushedRoot !== null && nextFlushedExpirationTime !== NoWork && minExpirationTime <= nextFlushedExpirationTime)
为 true,所以会再次渲染。有一个问题是:remainingExpirationTime(root.expirationTime)什么时候赋值的。经过分析,是通过以下步骤得到值得:
setState(enqueueSetState) => computeExpirationForFiber => scheduleWork => markPendingPriorityLevel
setState 异步
如果熟悉 setState 批量更新,setState 异步也很好理解了。需要注意的是这里的异步是通过同步达到的(异步代表的是一种效果),在官网中有这么一段话:
setState 是请求而不是执行更新的命令,所以存在异步。比如:
在 clickButton 函数中 this.state.data 函数输出 0 而非 1。setState 批量更新分析中已经知道,执行完 clickButton 函数之后才会更新,所以在 clickButton 是拿不到最新的 state。当然在 setState 是可以取得最新值的。
原生事件和 setTimeout 中的 setState
在原生事件和 setTimeout 中的 setState 是没有机制来保证批量更新和异步的,避免在原生事件和 setTimeout 中使用 setState。
在原生事件中,setState 是没有批量更新和异步效果。
在 setTimeout 中 setState 也会失去批量更新和异步效果,即使 setState 在合成事件或者生命周期函数中。setTimeout 这样的异步函数在 event loop 模型下,会在最后才能执行,此时 React 中确保 setState 批量更新和异步的机制早已失效。
需要我们注意的是:setState 在合成事件和生命周期函数中有异步的效果,在原生事件和 setTimeout 中是没有异步的。setState 异步是通过同步实现的。
The text was updated successfully, but these errors were encountered: