// In order to support react-lifecycles-compat polyfilled components, // Unsafe lifecycles should not be invoked for components using the new APIs. // 不应该使用旧的生命周期钩子 if ( typeof ctor.getDerivedStateFromProps !== 'function' && typeof instance.getSnapshotBeforeUpdate !== 'function' && (typeof instance.UNSAFE_componentWillMount === 'function' || typeof instance.componentWillMount === 'function') ) { # 触发WillMount生命周期钩子 callComponentWillMount(workInProgress, instance); // If we had additional state updates during this life-cycle, let's // process them now. processUpdateQueue(workInProgress, newProps, instance, renderLanes); instance.state = workInProgress.memoizedState; }
# 如果设置了class组件的componentDidMount生命周期钩子函数,则需要在组件的FiberNode上设置对应的flags if (typeof instance.componentDidMount === 'function') { let fiberFlags: Flags = Update; if (enableSuspenseLayoutEffectSemantics) { fiberFlags |= LayoutStatic; }
exportfunctionperformSyncWorkOnRoot(root: FiberRoot, lanes: Lanes): null{ if ((executionContext & (RenderContext | CommitContext)) !== NoContext) { thrownewError('Should not already be working.'); }
const didFlushPassiveEffects = flushPassiveEffects(); if (didFlushPassiveEffects) { // If passive effects were flushed, exit to the outer work loop in the root // scheduler, so we can recompute the priority. // TODO: We don't actually need this `ensureRootIsScheduled` call because // this path is only reachable if the root is already part of the schedule. // I'm including it only for consistency with the other exit points from // this function. Can address in a subsequent refactor. ensureRootIsScheduled(root); returnnull; }
if (enableProfilerTimer && enableProfilerNestedUpdatePhase) { syncNestedUpdateFlag(); }
// * setState // !1. render阶段 let exitStatus = renderRootSync(root, lanes); if (root.tag !== LegacyRoot && exitStatus === RootErrored) { // If something threw an error, try rendering one more time. We'll render // synchronously to block concurrent data mutations, and we'll includes // all pending updates are included. If it still fails after the second // attempt, we'll give up and commit the resulting tree. const originallyAttemptedLanes = lanes; const errorRetryLanes = getLanesToRetrySynchronouslyOnError( root, originallyAttemptedLanes, ); if (errorRetryLanes !== NoLanes) { lanes = errorRetryLanes; exitStatus = recoverFromConcurrentError( root, originallyAttemptedLanes, errorRetryLanes, ); } }
if (exitStatus === RootDidNotComplete) { // The render unwound without completing the tree. This happens in special // cases where need to exit the current render without producing a // consistent tree or committing. markRootSuspended(root, lanes, workInProgressDeferredLane); ensureRootIsScheduled(root); returnnull; }
// We now have a consistent tree. Because this is a sync render, we // will commit it even if something suspended. const finishedWork: Fiber = (root.current.alternate: any); root.finishedWork = finishedWork; root.finishedLanes = lanes; // ! 2. commit阶段 commitRoot( root, workInProgressRootRecoverableErrors, workInProgressTransitions, workInProgressRootDidIncludeRecursiveRenderUpdate, workInProgressDeferredLane, );
// Before exiting, make sure there's a callback scheduled for the next // pending level. ensureRootIsScheduled(root);
// This is always non-null on a ClassComponent or HostRoot const queue: UpdateQueue<State> = (workInProgress.updateQueue: any);
hasForceUpdate = false;
let firstBaseUpdate = queue.firstBaseUpdate; let lastBaseUpdate = queue.lastBaseUpdate;
// Check if there are pending updates. If so, transfer them to the base queue. // *1. 检查是否有pending update。如果有,需要将它们剪开环型链表,合并到 baseQueue。 // pending update是个单向循环链表,转移到 单链表 firstBaseUpdate->...->lastBaseUpdate 中去 // ! 这里注意pending update不同于baseQueue,pending update只记录了尾节点 let pendingQueue = queue.shared.pending; if (pendingQueue !== null) { // ? sy queue.shared.pending = null;
// The pending queue is circular. Disconnect the pointer between first // and last so that it's non-circular. // ! 这里的 pendingQueue 是在 finishQueueingConcurrentUpdates 函数中构建的单向循环链表 const lastPendingUpdate = pendingQueue; // 尾节点 const firstPendingUpdate = lastPendingUpdate.next; // 头结点 lastPendingUpdate.next = null; // 断开循环链表 // Append pending updates to base queue // 把pending update转移到base queue上 // ! 接下来构建单链表 firstBaseUpdate->...->lastBaseUpdate if (lastBaseUpdate === null) { // base queue是空的,那么firstPendingUpdate就是头节点firstBaseUpdate firstBaseUpdate = firstPendingUpdate; } else { // 否则往这个尾节点后继续加 lastBaseUpdate.next = firstPendingUpdate; } // 更新尾节点, 将 lastBaseUpdate 赋值为 lastPendingUpdate // 此时已经形成了 以 firstBaseUpdate 为头以 lastBaseUpdate 为尾的新链表 // 也即为本次需要处理的 update 链表 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 const current = workInProgress.alternate; // 如果有current queue,并且它和base queue不同,那么我们也需要把更新转移到那个queue上。 if (current !== null) { // This is always non-null on a ClassComponent or HostRoot // 类组件和 HostRoot 的updateQueue都初始化过,所以这里不会是null const currentQueue: UpdateQueue<State> = (current.updateQueue: any); const currentLastBaseUpdate = currentQueue.lastBaseUpdate; // 如果current的lastBaseUpdate和baseQueue的lastBaseUpdate不同,那么把pending update转移到currentQueue上 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) { // *2. 接下来要做的就是遍历queue,然后根据这些update,计算出最后的结果。 // 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. let newLanes = NoLanes; // 这里的 newBaseState, newFirstBaseUpdate,newLastBaseUpdate 是计算的临时变量 // 实际上会用来更新 updateQueue 的 baseState, firstBaseUpdate, lastBaseUpdate 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. 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. const shouldSkipUpdate = isHiddenUpdate ? !isSubsetOfLanes(getWorkInProgressRootRenderLanes(), updateLane) : !isSubsetOfLanes(renderLanes, updateLane);
// 更新优先级不满足,该 update 会被跳过 if (shouldSkipUpdate) { // ? sy-no // 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,
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 { // 该 update 更新优先级满足,本次更新不会跳过 // 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.
// 如果 newLastBaseUpdate 不存在,说明之前没有跳过任何 upadte 无需添加新增 // 否则无论无论该 update 是否跳过都需要添加到 baseUpdate 链表之后 if (newLastBaseUpdate !== null) { // ? sy-no 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. lane: NoLane,
tag: update.tag, payload: update.payload,
// When this update is rebased, we should not fire its // callback again. callback: null,
if (firstBaseUpdate === null) { // `queue.lanes` is used for entangling transitions. We can set it back to // zero once the queue is empty. // 当多个transitions在同一个queue中时,只允许最近的一个完成。我们不应该显示中间状态。 // 当queue为空,我们将queue.lanes设置回0 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. // 把跳过的update的lane记录下来 markSkippedUpdateLanes(newLanes); workInProgress.lanes = newLanes; workInProgress.memoizedState = newState; // 更新状态 } }