React 源码讲解第 5 节- Update 对象
根据第三节和第四节的内容,创建了 RootFiber 对象和 FiberRoot 对象之后,接下来就是处理更新。
源码
function updateContainer(element, container, parentComponent, callback) {
{
onScheduleRoot(container, element);
}
var current$1 = container.current;
var currentTime = requestCurrentTimeForUpdate();
{
// $FlowExpectedError - jest isn't a global, and isn't recognized outside of tests
if ('undefined' !== typeof jest) {
warnIfUnmockedScheduler(current$1);
warnIfNotScopedWithMatchingAct(current$1);
}
}
var suspenseConfig = requestCurrentSuspenseConfig();
var expirationTime = computeExpirationForFiber(currentTime, current$1, suspenseConfig);
var context = getContextForSubtree(parentComponent);
if (container.context === null) {
container.context = context;
} else {
container.pendingContext = context;
}
{
if (isRendering && current !== null && !didWarnAboutNestedUpdates) {
didWarnAboutNestedUpdates = true;
error('Render methods should be a pure function of props and state; ' + 'triggering nested component updates from render is not allowed. ' + 'If necessary, trigger nested updates in componentDidUpdate.\n\n' + 'Check the render method of %s.', getComponentName(current.type) || 'Unknown');
}
}
var update = createUpdate(expirationTime, suspenseConfig); // Caution: React DevTools currently depends on this property
// being called "element".
update.payload = {
element: element
};
callback = callback === undefined ? null : callback;
if (callback !== null) {
{
if (typeof callback !== 'function') {
error('render(...): Expected the last optional `callback` argument to be a ' + 'function. Instead received: %s.', callback);
}
}
update.callback = callback;
}
enqueueUpdate(current$1, update);
scheduleWork(current$1, expirationTime);
return expirationTime;
}
在 updateContainer
中 var update = createUpdate(expirationTime, suspenseConfig)
提炼出 update 对象。
Update 数据结构
fiber reconciler 将 fiber 的状态更新抽象为 Update 单向链表。
function createUpdate(expirationTime, suspenseConfig) {
var update = {
// 更新的过期时间
expirationTime: expirationTime,
// suspense 配置
suspenseConfig: suspenseConfig,
// 更新类型,见如下Update 的四种类型
tag: UpdateState,
// 更新内容,比如`setState`接收的第一个参数
payload: null,
// 对应的回调,`setState`,`render`都有
callback: null,
// 指向下一个更新
next: null
};
update.next = update;
{
update.priority = getCurrentPriorityLevel();//获取优先级等级
}
return update;
}
Update 的四种类型
export const UpdateState = 0;//基于 prevState 以及 payload 增量更新
export const ReplaceState = 1;//基于 payload 全量更新
export const ForceUpdate = 2;//强制更新,state 值依旧取 prevState,同时 hasForceUpdate 会被置为 true。
export const CaptureUpdate = 3;//将 fiber.effectTag 置为捕获更新
UpdateQueue 数据结构
在 updateContainer
中 enqueueUpdate(current$1, update)
提炼出 UpdateQueue 对象。
fiber reconciler 使用 UpdateQueue 存储 Update 更新队列。更新队列有两条,baseQueue 执行中的更新队列,pendingQueue(即 shared.pending)待执行的更新队列。因为 Update 表现为环状单向链表,baseQueue、pendingQueue 均存储单向链表的尾节点。丢弃更新作业的实现在于,将 pendingQueue 复制给 baseQueue,丢弃之前的 baseQueue(current fiber 和 work-in-progress fiber 均会重置 baseQueue)。
function enqueueUpdate(fiber, update) {
//存储执行中的更新任务 Update 队列,尾节点存储形式
var updateQueue = fiber.updateQueue;
if (updateQueue === null) {
// Only occurs if the fiber has been unmounted.
return;
}
//以 pending 属性存储待执行的更新任务 Update 队列,尾节点存储形式
var sharedQueue = updateQueue.shared;
var pending = sharedQueue.pending;
if (pending === null) {
// 第一次更新,创建一个循环列表
update.next = update;
} else {
update.next = pending.next;
pending.next = update;
}
sharedQueue.pending = update;
{
if (currentlyProcessingQueue === sharedQueue && !didWarnUpdateInsideUpdate) {
error('An update (setState, replaceState, or forceUpdate) was scheduled ' + 'from inside an update function. Update functions should be pure, ' + 'with zero side-effects. Consider using componentDidUpdate or a ' + 'callback.');
didWarnUpdateInsideUpdate = true;
}
}
}
fiber reconciler 会在挂载组件时调用 initializeUpdateQueue 函数初始化 fiber 节点的 updateQueue 队列。只有挂载的组件才会有有效的状态更新,卸载的组件没必要使用 updateQueue 属性。enqueueUpdate 函数将 update 添加到 pendingQueue 队列中,典型如类组件在 setState 方法调用期间将 update 添加到 pendingQueue 中。