RF 类更新队列
UpdateQueue is a linked list of prioritized updates.
UpdateQueue 是一个按优先级排序的更新链表。
Like fibers, update queues come in pairs: a current queue, which represents the visible state of the screen, and a work-in-progress queue, which can be mutated and processed asynchronously before it is committed — a form of double buffering. If a work-in-progress render is discarded before finishing, we create a new work-in-progress by cloning the current queue.
像 fiber 一样,更新队列成对存在:一个是当前队列,表示屏幕的可见状态;另一个是进行中的队列,可以在提交之前进行异步修改和处理——这是一种双缓冲的形式。如果一个进行中的渲染在完成之前被丢弃,我们会通过克隆当前队列来创建新的进行中队列。
Both queues share a persistent, singly-linked list structure. To schedule an update, we append it to the end of both queues. Each queue maintains a pointer to first update in the persistent list that hasn't been processed. The work-in-progress pointer always has a position equal to or greater than the current queue, since we always work on that one. The current queue's pointer is only updated during the commit phase, when we swap in the work-in-progress.
这两个队列共享一个持久的单向链表结构。要调度更新,我们将其追加到两个队列的末尾。每个队列都维护一个指针,指向持久列表中尚未处理的第一个更新。正在进行的工作指针的位置总是大于或等于当前队列的指针,因为我们总是处理正在进行的队列。当前队列的指针仅在提交阶段更新,当我们交换正在进行的队列时才会更新。
For example:
Current pointer: A - B - C - D - E - F
Work-in-progress pointer: D - E - F
^
The work-in-progress queue has
processed more updates than current.
例如:
但前指针: A - B - C - D - E - F
工作树指针: D - E - F
^
正在进行的队列处理的更新比当前更多
The reason we append to both queues is because otherwise we might drop updates without ever processing them. For example, if we only add updates to the work-in-progress queue, some updates could be lost whenever a work-in -progress render restarts by cloning from current. Similarly, if we only add updates to the current queue, the updates will be lost whenever an already in-progress queue commits and swaps with the current queue. However, by adding to both queues, we guarantee that the update will be part of the next work-in-progress. (And because the work-in-progress queue becomes the current queue once it commits, there's no danger of applying the same update twice.)
我们之所以向两个队列都添加,是因为否则我们可能会丢失更新而无法处理它们。例如,如果我们只向正在进行的队列添加更新,那么每当一个正在进行的渲染通过从当前状态克隆来重新启动时,一些更新可能会丢失。同样,如果我们只向当前队列添加更新,那么每当一个已在进行中的队列提交并与当前队列交换时,这些更新将会丢失。然而,通过向两个队列都添加,我们可以保证更新会成为下一个正在进行的工作的一部分。(并且因为一旦正在进行的队列提交,它会成为当前队列,所以不会有同一个更新被应用两次的危险。)
一、Prioritization (优先级排序)
Updates are not sorted by priority, but by insertion; new updates are always appended to the end of the list.
更新不是按优先级排序的,而是按插入顺序;新的更新总是附加到列表的末尾。
The priority is still important, though. When processing the update queue during the render phase, only the updates with sufficient priority are included in the result. If we skip an update because it has insufficient priority, it remains in the queue to be processed later, during a lower priority render. Crucially, all updates subsequent to a skipped update also remain in the queue regardless of their priority. That means high priority updates are sometimes processed twice, at two separate priorities. We also keep track of a base state, that represents the state before the first update in the queue is applied.
优先级仍然很重要。在渲染阶段处理更新队列时,只有具有足够优先级的更新会被包括在结果中。如果我们因为优先级不足而跳过某个更新,它会留在队列中,以便在优先级较低的渲染过程中稍后处理。关键是,所有在被跳过的更新之后的更新也会留在队列中,无论它们的优先级如何。这意味着高优先级的更新有时会在两个不同的优先级下被处理两次。我们还会跟踪一个基本状态,它表示在队列中第一个更新应用之前的状态。
For example:
例如:
Given a base state of '', and the following queue of updates
给定一个基本状态为 '',以及以下更新队列
A1 - B2 - C1 - D2
where the number indicates the priority, and the update is applied to the previous state by appending a letter, React will process these updates as two separate renders, one per distinct priority level:
数字表示优先级,更新通过添加一个字母应用到先前的状态,React 会将这些更新作为两个独立的渲染来处理,每个不同的优先级级别一个:
- First render, at priority 1:
Base state: ''
Updates: [A1, C1]
Result state: 'AC' - 首次渲染,优先级 1:
基础状态: ''
更 新: [A1, C1]
结果状态: 'AC' - Second render, at priority 2:
Base state: 'A' <- The base state does not include C1,
because B2 was skipped.
Updates: [B2, C1, D2] <- C1 was rebased on top of B2
Result state: 'ABCD' - 第二次渲染,优先级为 2:
基础状态: 'A' <- 基础状态不包括C1,因为跳过了B2。
更 新: [B2, C1, D2] <- C1 已经基于 B2 重新变基
结果状态: 'ABCD'
Because we process updates in insertion order, and rebase high priority updates when preceding updates are skipped, the final result is deterministic regardless of priority. Intermediate state may vary according to system resources, but the final state is always the same.
因为我们按照插入顺序处理更新,并在跳过前面的更新时重新基于高优先级更新,所以最终结果是确定的,无论优先级如何。中间状态可能会根据系统资源而变化,但最终状态总是相同的。
二、导出的常量
1. 更新状态
export const UpdateState = 0;
2. 替换状态
export const ReplaceState = 1;
3. 强制更新
export const ForceUpdate = 2;
4. 捕获更新
export const CaptureUpdate = 3;
三、导出的变量
1. 重置当前处理队列
其实是一个函数
export let resetCurrentlyProcessingQueue: () => void;
if (__DEV__) {
didWarnUpdateInsideUpdate = false;
currentlyProcessingQueue = null;
resetCurrentlyProcessingQueue = () => {
currentlyProcessingQueue = null;
};
}
四、初始化更新队列
export function initializeUpdateQueue<State>(fiber: Fiber): void {
const queue: UpdateQueue<State> = {
baseState: fiber.memoizedState,
firstBaseUpdate: null,
lastBaseUpdate: null,
shared: {
pending: null,
lanes: NoLanes,
hiddenCallbacks: null,
},
callbacks: null,
};
fiber.updateQueue = queue;
}
五、克隆更新队列
export function cloneUpdateQueue<State>(
current: Fiber,
workInProgress: Fiber,
): void {
// Clone the update queue from current. Unless it's already a clone.
// 从当前克隆更新队列。除非它已经是克隆。
const queue: UpdateQueue<State> = workInProgress.updateQueue as any;
const currentQueue: UpdateQueue<State> = current.updateQueue as any;
if (queue === currentQueue) {
const clone: UpdateQueue<State> = {
baseState: currentQueue.baseState,
firstBaseUpdate: currentQueue.firstBaseUpdate,
lastBaseUpdate: currentQueue.lastBaseUpdate,
shared: currentQueue.shared,
callbacks: null,
};
workInProgress.updateQueue = clone;
}
}
六、创建更新
export function createUpdate(lane: Lane): Update<mixed> {
const update: Update<mixed> = {
lane,
tag: UpdateState,
payload: null,
callback: null,
next: null,
};
return update;
}
七、入队更新
getComponentNameFromFiber()由 getComponentNameFromFiber 实现isUnsafeClassRenderPhaseUpdate()由 ReactFiberWorkLoop 提供unsafe_markUpdateLaneFromFiberToRoot()由 ReactFiberConcurrentUpdates#unsafe_markUpdateLaneFromFiberToRoot 实现enqueueConcurrentClassUpdate()由 ReactFiberConcurrentUpdates#enqueueConcurrentClassUpdate 实现
export function enqueueUpdate<State>(
fiber: Fiber,
update: Update<State>,
lane: Lane,
): FiberRoot | null {
const updateQueue = fiber.updateQueue;
if (updateQueue === null) {
// Only occurs if the fiber has been unmounted.
// 仅在 fiber 已被卸载时发生。
return null;
}
const sharedQueue: SharedQueue<State> = (updateQueue as any).shared;
if (__DEV__) {
if (
currentlyProcessingQueue === sharedQueue &&
!didWarnUpdateInsideUpdate
) {
const componentName = getComponentNameFromFiber(fiber);
console.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.\n\nPlease update the following component: %s',
componentName,
);
didWarnUpdateInsideUpdate = true;
}
}
if (isUnsafeClassRenderPhaseUpdate(fiber)) {
// This is an unsafe render phase update. Add directly to the update
// queue so we can process it immediately during the current render.
// 这是一次不安全的渲染阶段更新。直接添加到更新队列中,以便我们可以在当前
// 渲染期间立即处理它。
const pending = sharedQueue.pending;
if (pending === null) {
// This is the first update. Create a circular list.
// 这是第一次更新。创建一个循环列表。
update.next = update;
} else {
update.next = pending.next;
pending.next = update;
}
sharedQueue.pending = update;
// Update the childLanes even though we're most likely already rendering
// this fiber. This is for backwards compatibility in the case where you
// update a different component during render phase than the one that is
// currently renderings (a pattern that is accompanied by a warning).
//
// 更新 childLanes,即使我们很可能已经在渲染这个 fiber。
// 这是为了向后兼容,以防你在渲染阶段更新了与当前正在渲染的组件不同的
// 组件(这种模式会伴随一个警告)。
return unsafe_markUpdateLaneFromFiberToRoot(fiber, lane);
} else {
return enqueueConcurrentClassUpdate(fiber, sharedQueue, update, lane);
}
}
八、纠缠过渡
isTransitionLane()由 ReactFiberLane#isTransitionLane 实现intersectLanes()由 ReactFiberLane#intersectLanes 实现mergeLanes()由 ReactFiberLane#mergeLanes 实现markRootEntangled()由 ReactFiberLane#markRootEntangled 实现
export function entangleTransitions(root: FiberRoot, fiber: Fiber, lane: Lane) {
const updateQueue = fiber.updateQueue;
if (updateQueue === null) {
// Only occurs if the fiber has been unmounted.
// 仅在 fiber 已被卸载时发生。
return;
}
const sharedQueue: SharedQueue<mixed> = (updateQueue as any).shared;
if (isTransitionLane(lane)) {
let queueLanes = sharedQueue.lanes;
// If any entangled lanes are no longer pending on the root, then they must
// have finished. We can remove them from the shared queue, which represents
// a superset of the actually pending lanes. In some cases we may entangle
// more than we need to, but that's OK. In fact it's worse if we *don't*
// entangle when we should.
// If any entangled lanes are no longer pending on the root, then they must
// have finished. We can remove them from the shared queue, which represents
// a superset of the actually pending lanes. In some cases we may entangle
// more than we need to, but that's OK. In fact it's worse if we *don't*
// entangle when we should.
queueLanes = intersectLanes(queueLanes, root.pendingLanes);
// Entangle the new transition lane with the other transition lanes.
// 将新的过渡车道与其他过渡车道交织在一起。
const newQueueLanes = mergeLanes(queueLanes, lane);
sharedQueue.lanes = newQueueLanes;
// Even if queue.lanes already include lane, we don't know for certain if
// the lane finished since the last time we entangled it. So we need to
// entangle it again, just to be sure.
//
// 即使 queue.lanes 已经包含该通道,我们也无法确定自上次纠缠以来该通道是否已经完成。因此
// 我们需要再次纠缠它,以确保万无一失。
markRootEntangled(root, newQueueLanes);
}
}
九、排队处理捕获的更新
export function enqueueCapturedUpdate<State>(
workInProgress: Fiber,
capturedUpdate: Update<State>,
) {
// Captured updates are updates that are thrown by a child during the render
// phase. They should be discarded if the render is aborted. Therefore,
// we should only put them on the work-in-progress queue, not the current one.
//
// 捕获的更新是子组件在渲染阶段抛出的更新
// 如果渲染被中止,这些更新应该被丢弃。因此,
// 我们只应将它们放入正在进行的工作队列,而不是当前队列。
let queue: UpdateQueue<State> = workInProgress.updateQueue as any;
// Check if the work-in-progress queue is a clone.
// 检查正在进行的队列是否为克隆。
const current = workInProgress.alternate;
if (current !== null) {
const currentQueue: UpdateQueue<State> = current.updateQueue as any;
if (queue === currentQueue) {
// The work-in-progress queue is the same as current. This happens when
// we bail out on a parent fiber that then captures an error thrown by
// a child. Since we want to append the update only to the work-in
// -progress queue, we need to clone the updates. We usually clone during
// processUpdateQueue, but that didn't happen in this case because we
// skipped over the parent when we bailed out.
//
// 正在进行的工作队列与当前队列相同。当我们在父 fiber 上中止时,而父 fiber 又捕获了
// 子 fiber 抛出的错误,就会发生这种情况。由于我们只想将更新追加到正在进行的工作队列
// 中,因此需要克隆这些更新。我们通常在 processUpdateQueue 过程中进行克隆,但在这种
// 情况下没有发生,因为我们在中止时跳过了父节点。
let newFirst = null;
let newLast = null;
const firstBaseUpdate = queue.firstBaseUpdate;
if (firstBaseUpdate !== null) {
// Loop through the updates and clone them.
// 遍历更新并克隆它们。
let update: Update<State> = firstBaseUpdate;
do {
const clone: Update<State> = {
lane: update.lane,
tag: update.tag,
payload: update.payload,
// When this update is rebased, we should not fire its
// callback again.
// 当此更新被重新基准化时,我们不应再次触发其回调。
callback: null,
next: null,
};
if (newLast === null) {
newFirst = newLast = clone;
} else {
newLast.next = clone;
newLast = clone;
}
update = update.next;
} while (update !== null);
// Append the captured update the end of the cloned list.
// 将捕获的更新附加到克隆列表的末尾。
if (newLast === null) {
newFirst = newLast = capturedUpdate;
} else {
newLast.next = capturedUpdate;
newLast = capturedUpdate;
}
} else {
// There are no base updates.
// 没有基础更新。
newFirst = newLast = capturedUpdate;
}
queue = {
baseState: currentQueue.baseState,
firstBaseUpdate: newFirst,
lastBaseUpdate: newLast,
shared: currentQueue.shared,
callbacks: currentQueue.callbacks,
};
workInProgress.updateQueue = queue;
return;
}
}
// Append the update to the end of the list.
// 将更新附加到列表末尾。
const lastBaseUpdate = queue.lastBaseUpdate;
if (lastBaseUpdate === null) {
queue.firstBaseUpdate = capturedUpdate;
} else {
lastBaseUpdate.next = capturedUpdate;
}
queue.lastBaseUpdate = capturedUpdate;
}
十、如果更新则挂起从纠缠异步操作读取
peekEntangledActionThenable()由 ReactFiberAsyncAction#peekEntangledActionThenable 实现
// Each call to processUpdateQueue should be accompanied by a call to this. It's
// only in a separate function because in updateHostRoot, it must happen after
// all the context stacks have been pushed to, to prevent a stack mismatch. A
// bit unfortunate.
//
// 每次调用 processUpdateQueue 时都应伴随调用此函数。它之所以作为一个单独的函数,是因为
// 在 updateHostRoot 中,必须在所有上下文栈都已推入之后才调用,以防止栈不匹配。有点不太理想。
export function suspendIfUpdateReadFromEntangledAsyncAction() {
// Check if this update is part of a pending async action. If so, we'll
// need to suspend until the action has finished, so that it's batched
// together with future updates in the same action.
// TODO: Once we support hooks inside useMemo (or an equivalent
// memoization boundary like Forget), hoist this logic so that it only
// suspends if the memo boundary produces a new value.
//
// 检查此更新是否属于待处理的异步操作。如果是,我们需要挂起,直到该操作完成,这样它就可以与
// 同一操作中的后续更新一起批处理。
// TODO: 一旦我们支持在 useMemo(或类似的记忆边界,如 Forget)内部使用 hooks,将此逻辑提
// 升,这样只有当记忆边界生成新值时才会挂起。
if (didReadFromEntangledAsyncAction) {
const entangledActionThenable = peekEntangledActionThenable();
if (entangledActionThenable !== null) {
// TODO: Instead of the throwing the thenable directly, throw a
// special object like `use` does so we can detect if it's captured
// by userspace.
//
// TODO:不要直接抛出 thenable,而是抛出类似 `use` 的特殊对象,这样我们
// 就可以检测它是否被用户空间捕获。
throw entangledActionThenable;
}
}
}
十一、处理更新队列
removeLanes()由 ReactFiberLane#removeLanes 实现mergeLanes()由 ReactFiberLane#mergeLanes 实现isSubsetOfLanes()由 ReactFiberLane#isSubsetOfLanes 实现peekEntangledActionLane()由 ReactFiberAsyncAction#peekEntangledActionLane 实现markSkippedUpdateLanes()由 ReactFiberWorkLoop 提供
export function processUpdateQueue<State>(
workInProgress: Fiber,
props: any,
instance: any,
renderLanes: Lanes,
): void {
didReadFromEntangledAsyncAction = false;
// This is always non-null on a ClassComponent or HostRoot
// 在类组件或 HostRoot 上,这始终是非空的
const queue: UpdateQueue<State> = workInProgress.updateQueue as any;
hasForceUpdate = false;
if (__DEV__) {
currentlyProcessingQueue = queue.shared;
}
let firstBaseUpdate = queue.firstBaseUpdate;
let lastBaseUpdate = queue.lastBaseUpdate;
// Check if there are pending updates. If so, transfer them to the base queue.
// 检查是否有待处理的更新。如果有,将它们转移到基础队列。
let pendingQueue = queue.shared.pending;
if (pendingQueue !== null) {
queue.shared.pending = null;
// The pending queue is circular. Disconnect the pointer between first
// and last so that it's non-circular.
//
// 待处理队列是循环的。断开第一个和最后一个之间的指针,使其变为非循环队列。
const lastPendingUpdate = pendingQueue;
const firstPendingUpdate = lastPendingUpdate.next;
lastPendingUpdate.next = null;
// Append pending updates to base queue
// 将待处理的更新附加到基本队列
if (lastBaseUpdate === null) {
firstBaseUpdate = firstPendingUpdate;
} else {
lastBaseUpdate.next = firstPendingUpdate;
}
lastBaseUpdate = lastPendingUpdate;
// If there's a current queue, and it's different from the base queue, then
// we need to transfer the updates to that queue, too. Because the base
// queue is a singly-linked list with no cycles, we can append to both
// lists and take advantage of structural sharing.
// TODO: Pass `current` as argument
//
// 如果当前有队列,并且它与基础队列不同,那么
// 我们也需要将更新传递到该队列。因为基础
// 队列是一个没有循环的单链表,我们可以同时将元素追加到两个列表中,并利用结构共享。
// TODO: 将 `current` 作为参数传递
const current = workInProgress.alternate;
if (current !== null) {
// This is always non-null on a ClassComponent or HostRoot
// 在 ClassComponent 或 HostRoot 上,这始终是非空的
const currentQueue: UpdateQueue<State> = current.updateQueue as any;
const currentLastBaseUpdate = currentQueue.lastBaseUpdate;
if (currentLastBaseUpdate !== lastBaseUpdate) {
if (currentLastBaseUpdate === null) {
currentQueue.firstBaseUpdate = firstPendingUpdate;
} else {
currentLastBaseUpdate.next = firstPendingUpdate;
}
currentQueue.lastBaseUpdate = lastPendingUpdate;
}
}
}
// These values may change as we process the queue.
// 随着我们处理队列,这些值可能会发生变化。
if (firstBaseUpdate !== null) {
// Iterate through the list of updates to compute the result.
// 遍历更新列表以计算结果。
let newState = queue.baseState;
// TODO: Don't need to accumulate this. Instead, we can remove renderLanes
// from the original lanes.
//
// TODO: 不需要累积这个。相反,我们可以从原始 lanes 中移除 renderLanes。
let newLanes: Lanes = NoLanes;
let newBaseState = null;
let newFirstBaseUpdate = null;
let newLastBaseUpdate: null | Update<State> = null;
let update: Update<State> = firstBaseUpdate;
do {
// An extra OffscreenLane bit is added to updates that were made to
// a hidden tree, so that we can distinguish them from updates that were
// already there when the tree was hidden.
//
// 对隐藏树进行的更新会增加一个额外的 OffscreenLane 位,以便我们可以将它们
// 与树隐藏时已经存在的更新区分开来。
const updateLane = removeLanes(update.lane, OffscreenLane);
const isHiddenUpdate = updateLane !== update.lane;
// Check if this update was made while the tree was hidden. If so, then
// it's not a "base" update and we should disregard the extra base lanes
// that were added to renderLanes when we entered the Offscreen tree.
//
// 检查此更新是否在树隐藏时进行。如果是这样,
// 那么它不是“基础”更新,我们应该忽略在进入离屏树时
// 添加到 renderLanes 的额外基础车道。
const shouldSkipUpdate = isHiddenUpdate
? !isSubsetOfLanes(getWorkInProgressRootRenderLanes(), updateLane)
: !isSubsetOfLanes(renderLanes, updateLane);
if (shouldSkipUpdate) {
// Priority is insufficient. Skip this update. If this is the first
// skipped update, the previous update/state is the new base
// update/state.
//
// 优先级不足。跳过此更新。如果这是第一次跳过的更新,则上一次更新/状态是
// 新的基础更新/状态。
const clone: Update<State> = {
lane: updateLane,
tag: update.tag,
payload: update.payload,
callback: update.callback,
next: null,
};
if (newLastBaseUpdate === null) {
newFirstBaseUpdate = newLastBaseUpdate = clone;
newBaseState = newState;
} else {
newLastBaseUpdate = newLastBaseUpdate.next = clone;
}
// Update the remaining priority in the queue.
// 更新队列中剩余的优先级。
newLanes = mergeLanes(newLanes, updateLane);
} else {
// This update does have sufficient priority.
// 这个更新确实有足够的优先级。
// Check if this update is part of a pending async action. If so,
// we'll need to suspend until the action has finished, so that it's
// batched together with future updates in the same action.
//
// 检查此更新是否属于待处理的异步操作。如果是,
// 我们需要暂停,直到操作完成,以便将其与同一操作中的未来更新一起批处理。
if (updateLane !== NoLane && updateLane === peekEntangledActionLane()) {
didReadFromEntangledAsyncAction = true;
}
if (newLastBaseUpdate !== null) {
const clone: Update<State> = {
// This update is going to be committed so we never want uncommit
// it. Using NoLane works because 0 is a subset of all bitmasks, so
// this will never be skipped by the check above.
//
// 这个更新将会被提交,所以我们永远不想取消提交它。
// 使用 NoLane 可行,因为 0 是所有位掩码的子集,
// 所以上面的检查永远不会跳过它。
lane: NoLane,
tag: update.tag,
payload: update.payload,
// When this update is rebased, we should not fire its
// callback again.
//
// 当此更新被重新基准化时,我们不应再次触发其回调。
callback: null,
next: null,
};
newLastBaseUpdate = newLastBaseUpdate.next = clone;
}
// Process this update.
// 处理此更新。
newState = getStateFromUpdate(
workInProgress,
queue,
update,
newState,
props,
instance,
);
const callback = update.callback;
if (callback !== null) {
workInProgress.flags |= Callback;
if (isHiddenUpdate) {
workInProgress.flags |= Visibility;
}
const callbacks = queue.callbacks;
if (callbacks === null) {
queue.callbacks = [callback];
} else {
callbacks.push(callback);
}
}
}
update = update.next;
if (update === null) {
pendingQueue = queue.shared.pending;
if (pendingQueue === null) {
break;
} else {
// An update was scheduled from inside a reducer. Add the new
// pending updates to the end of the list and keep processing.
//
// 在 reducer 内部安排了更新。将新的待处理更新添加到列表末尾,并继续处理。
const lastPendingUpdate = pendingQueue;
// Intentionally unsound. Pending updates form a circular list, but we
// unravel them when transferring them to the base queue.
//
// 故意不安全。待处理的更新形成一个循环列表,
// 但在将它们转移到基队列时会将其展开。
const firstPendingUpdate =
lastPendingUpdate.next as any as Update<State>;
lastPendingUpdate.next = null;
update = firstPendingUpdate;
queue.lastBaseUpdate = lastPendingUpdate;
queue.shared.pending = null;
}
}
} while (true);
if (newLastBaseUpdate === null) {
newBaseState = newState;
}
queue.baseState = newBaseState as any as State;
queue.firstBaseUpdate = newFirstBaseUpdate;
queue.lastBaseUpdate = newLastBaseUpdate;
if (firstBaseUpdate === null) {
// `queue.lanes` is used for entangling transitions. We can set it back to
// zero once the queue is empty.
//
// `queue.lanes` 用于处理交错的过渡。一旦队列为空,我们可以将其重置为零。
queue.shared.lanes = NoLanes;
}
// Set the remaining expiration time to be whatever is remaining in the queue.
// This should be fine because the only two other things that contribute to
// expiration time are props and context. We're already in the middle of the
// begin phase by the time we start processing the queue, so we've already
// dealt with the props. Context in components that specify
// shouldComponentUpdate is tricky; but we'll have to account for
// that regardless.
//
// 将剩余的过期时间设置为队列中剩下的时间。
// 这样做应该没问题,因为唯一影响过期时间的另外两个因素是 props 和 context。
// 当我们开始处理队列时,已经处于 begin 阶段中,因此 props 已经处理过了。
// 对于指定了 shouldComponentUpdate 的组件而言,context 会比较棘手;
// 但无论如何我们都必须考虑到这一点。
markSkippedUpdateLanes(newLanes);
workInProgress.lanes = newLanes;
workInProgress.memoizedState = newState;
}
if (__DEV__) {
currentlyProcessingQueue = null;
}
}
十二、在处理前重置强制更新标志
export function resetHasForceUpdateBeforeProcessing() {
hasForceUpdate = false;
}
十三、处理后检查是否强制更新
export function checkHasForceUpdateAfterProcessing(): boolean {
return hasForceUpdate;
}
十四、延迟隐藏回调
export function deferHiddenCallbacks<State>(
updateQueue: UpdateQueue<State>,
): void {
// When an update finishes on a hidden component, its callback should not
// be fired until/unless the component is made visible again. Stash the
// callback on the shared queue object so it can be fired later.
//
// 当隐藏组件的更新完成时,除非组件再次显示,否则其回调不应触发。
// 将回调存放在共享队列对象上,以便以后触发。
const newHiddenCallbacks = updateQueue.callbacks;
if (newHiddenCallbacks !== null) {
const existingHiddenCallbacks = updateQueue.shared.hiddenCallbacks;
if (existingHiddenCallbacks === null) {
updateQueue.shared.hiddenCallbacks = newHiddenCallbacks;
} else {
updateQueue.shared.hiddenCallbacks =
existingHiddenCallbacks.concat(newHiddenCallbacks);
}
}
}
十五、 提交隐藏回调
export function commitHiddenCallbacks<State>(
updateQueue: UpdateQueue<State>,
context: any,
): void {
// This component is switching from hidden -> visible. Commit any callbacks
// that were previously deferred.
//
// 该组件正在从隐藏状态切换到可见状态。提交之前延迟的任何回调。
const hiddenCallbacks = updateQueue.shared.hiddenCallbacks;
if (hiddenCallbacks !== null) {
updateQueue.shared.hiddenCallbacks = null;
for (let i = 0; i < hiddenCallbacks.length; i++) {
const callback = hiddenCallbacks[i];
callCallback(callback, context);
}
}
}
十六、 提交回调
export function commitCallbacks<State>(
updateQueue: UpdateQueue<State>,
context: any,
): void {
const callbacks = updateQueue.callbacks;
if (callbacks !== null) {
updateQueue.callbacks = null;
for (let i = 0; i < callbacks.length; i++) {
const callback = callbacks[i];
callCallback(callback, context);
}
}
}
十七、变量
1. 强制更新
// Global state that is reset at the beginning of calling `processUpdateQueue`.
// It should only be read right after calling `processUpdateQueue`, via
// `checkHasForceUpdateAfterProcessing`.
//
// 全局状态,在调用 `processUpdateQueue` 开始时会重置。
// 它只应在调用 `processUpdateQueue` 后通过
// `checkHasForceUpdateAfterProcessing` 读取。
let hasForceUpdate = false; // 强制更新
let didWarnUpdateInsideUpdate; // 在更新内部发出警告
let currentlyProcessingQueue: ?SharedQueue<$FlowFixMe>; // 当前处理队列
2. 已从纠缠异步操作读取
let didReadFromEntangledAsyncAction: boolean = false;
十八、工具
1. 从更新获取状态
enterDisallowedContextReadInDEV()由 ReactFiberNewContext#enterDisallowedContextReadInDEV 实现setIsStrictModeForDevtools()由 ReactFiberDevToolsHook#setIsStrictModeForDevtools 实现exitDisallowedContextReadInDEV()由 ReactFiberNewContext#exitDisallowedContextReadInDEV 实现
function getStateFromUpdate<State>(
workInProgress: Fiber,
queue: UpdateQueue<State>,
update: Update<State>,
prevState: State,
nextProps: any,
instance: any,
): any {
switch (update.tag) {
case ReplaceState: {
const payload = update.payload;
if (typeof payload === 'function') {
// Updater function
// 更新函数
if (__DEV__) {
enterDisallowedContextReadInDEV();
}
const nextState = payload.call(instance, prevState, nextProps);
if (__DEV__) {
if (workInProgress.mode & StrictLegacyMode) {
setIsStrictModeForDevtools(true);
try {
payload.call(instance, prevState, nextProps);
} finally {
setIsStrictModeForDevtools(false);
}
}
exitDisallowedContextReadInDEV();
}
return nextState;
}
// State object
// 状态对象
return payload;
}
case CaptureUpdate: {
workInProgress.flags =
(workInProgress.flags & ~ShouldCapture) | DidCapture;
}
// Intentional fallthrough
// 有意贯穿
case UpdateState: {
const payload = update.payload;
let partialState;
if (typeof payload === 'function') {
// Updater function
// 更新函数
if (__DEV__) {
enterDisallowedContextReadInDEV();
}
partialState = payload.call(instance, prevState, nextProps);
if (__DEV__) {
if (workInProgress.mode & StrictLegacyMode) {
setIsStrictModeForDevtools(true);
try {
payload.call(instance, prevState, nextProps);
} finally {
setIsStrictModeForDevtools(false);
}
}
exitDisallowedContextReadInDEV();
}
} else {
// Partial state object
// 部分状态对象
partialState = payload;
}
if (partialState === null || partialState === undefined) {
// Null and undefined are treated as no-ops.
// null 和 undefined 被视为无操作。
return prevState;
}
// Merge the partial state and the previous state.
// 合并部分状态和之前的状态。
return assign({}, prevState, partialState);
}
case ForceUpdate: {
hasForceUpdate = true;
return prevState;
}
}
return prevState;
}
2. 调用回调
function callCallback(callback: () => mixed, context: any) {
if (typeof callback !== 'function') {
throw new Error(
'Invalid argument passed as callback. Expected a function. Instead ' +
`received: ${callback}`,
);
}
callback.call(context);
}