exportfunctionrequestUpdateLane(fiber: Fiber): Lane{ // Special cases const mode = fiber.mode; // 1. 非ConcurrentMode模式 2. 目前不支持 if ((mode & ConcurrentMode) === NoMode) { return (SyncLane: Lane); } elseif ( (executionContext & RenderContext) !== NoContext && workInProgressRootRenderLanes !== NoLanes ) { // This is a render phase update. These are not officially supported. The // old behavior is to give this the same "thread" (lanes) as // whatever is currently rendering. So if you call `setState` on a component // that happens later in the same render, it will flush. Ideally, we want to // remove the special case and treat them as if they came from an // interleaved event. Regardless, this pattern is not officially supported. // This behavior is only a fallback. The flag only exists until we can roll // out the setState warning, since existing code might accidentally rely on // the current behavior. return pickArbitraryLane(workInProgressRootRenderLanes); }
// 普通更新与非紧急更新(18) const transition = requestCurrentTransition(); // 如果有transition if (transition !== null) { const actionScopeLane = peekEntangledActionLane(); return actionScopeLane !== NoLane ? // We're inside an async action scope. Reuse the same lane. actionScopeLane : // We may or may not be inside an async action scope. If we are, this // is the first update in that scope. Either way, we need to get a // fresh transition lane. requestTransitionLane(transition); }
// React内部的一些update,比如flushSync,会通过上下文变量来跟踪其优先级 const updateLane: Lane = (getCurrentUpdatePriority(): any); if (updateLane !== NoLane) { // ? sy setState click 2 return updateLane; }
exportfunctiongetEventPriority(domEventName: DOMEventName): EventPriority{ switch (domEventName) { // Used by SimpleEventPlugin: case'cancel': case'click': case'close': case'contextmenu': case'copy': case'cut': case'auxclick': case'dblclick': case'dragend': case'dragstart': case'drop': case'focusin': case'focusout': case'input': case'invalid': case'keydown': case'keypress': case'keyup': case'mousedown': case'mouseup': case'paste': case'pause': case'play': case'pointercancel': case'pointerdown': case'pointerup': case'ratechange': case'reset': case'resize': case'seeked': case'submit': case'touchcancel': case'touchend': case'touchstart': case'volumechange': // Used by polyfills: (fall through) case'change': case'selectionchange': case'textInput': case'compositionstart': case'compositionend': case'compositionupdate': // Only enableCreateEventHandleAPI: (fall through) case'beforeblur': case'afterblur': // Not used by React but could be by user code: (fall through) case'beforeinput': case'blur': case'fullscreenchange': case'focus': case'hashchange': case'popstate': case'select': case'selectstart': return DiscreteEventPriority; case'drag': case'dragenter': case'dragexit': case'dragleave': case'dragover': case'mousemove': case'mouseout': case'mouseover': case'pointermove': case'pointerout': case'pointerover': case'scroll': case'toggle': case'touchmove': case'wheel': // Not used by React but could be by user code: (fall through) case'mouseenter': case'mouseleave': case'pointerenter': case'pointerleave': return ContinuousEventPriority; case'message': { // We might be in the Scheduler callback. // Eventually this mechanism will be replaced by a check // of the current priority on the native scheduler. const schedulerPriority = getCurrentSchedulerPriorityLevel(); switch (schedulerPriority) { case ImmediateSchedulerPriority: return DiscreteEventPriority; case UserBlockingSchedulerPriority: return ContinuousEventPriority; case NormalSchedulerPriority: case LowSchedulerPriority: // TODO: Handle LowSchedulerPriority, somehow. Maybe the same lane as hydration. return DefaultEventPriority; case IdleSchedulerPriority: return IdleEventPriority; default: return DefaultEventPriority; } } default: return DefaultEventPriority; } }
let newBaseState = null; let newBaseQueueFirst = null; let newBaseQueueLast: Update<S, A> | null = null; // 跳过的这些更新(低优先级任务)会被保存在这个循环链表中 let update = first; let didReadFromEntangledAsyncAction = false; 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);
// 当前任务的优先级不够,也就是说当前的 renderLanes 比该 update 对象的优先级高。需要先跳过,之后再处理。 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<S, A> = { lane: updateLane, revertLane: update.revertLane, action: update.action, hasEagerState: update.hasEagerState, eagerState: update.eagerState, next: (null: any), }; // 如果在当前被跳过的 update 对象之前没有其他的 update 被跳过,该对象就是作为新的基础更新对象。并把最初跳过任务时候的 baseState 存储起来 if (newBaseQueueLast === null) { newBaseQueueFirst = newBaseQueueLast = clone; newBaseState = newState; } else { // 如果之前有更新被跳过,那么将这个更新对象添加到队列最后 newBaseQueueLast = newBaseQueueLast.next = clone; } // 更新当前正在工作的 Fiber 节点(workInProgress)的优先级,标记这个更新对象的优先级由于不匹配当前的 renderLane,因此已经被跳过。 // 在同文件的 renderWithHook() 方法中可以知道 currentlyRenderingFiber 对应了 workInProgress,表示当前正在工作的 fiber 树 currentlyRenderingFiber.lanes = mergeLanes( currentlyRenderingFiber.lanes, updateLane, ); markSkippedUpdateLanes(updateLane); } else { // 优先级足够 // 优先级足够的时候理论上可以考虑对哪些 update 对象进行收集更新,但是此时还需要考虑一个 revertLane(还原的优先级),以保证组件状态的正确。 // This update does have sufficient priority.
// Check if this is an optimistic update. const revertLane = update.revertLane; if (!enableAsyncActions || revertLane === NoLane) { // ? sy // This is not an optimistic update, and we're going to apply it now. // But, if there were earlier updates that were skipped, we need to // leave this update in the queue so it can be rebased later. // 如果 newBaseQueueLast 不为 null,证明有跳过的更新,要把当前的update也加入newBaseQueueLast只是到其 lane 会赋值为 0,后面进行低优先级的更新时,这些已经被处理过的更新也仍旧会被处理,保证最后计算的数据是正确的 if (newBaseQueueLast !== null) { const clone: Update<S, A> = { // 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, revertLane: NoLane, action: update.action, hasEagerState: update.hasEagerState, eagerState: update.eagerState, next: (null: any), }; newBaseQueueLast = newBaseQueueLast.next = clone; }
// 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 === peekEntangledActionLane()) { // ? sy-no didReadFromEntangledAsyncAction = true; } } else { // This is an optimistic update. If the "revert" priority is // sufficient, don't apply the update. Otherwise, apply the update, // but leave it in the queue so it can be either reverted or // rebased in a subsequent render. if (isSubsetOfLanes(renderLanes, revertLane)) { // The transition that this optimistic update is associated with // has finished. Pretend the update doesn't exist by skipping // over it. update = update.next;
// 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 (revertLane === peekEntangledActionLane()) { didReadFromEntangledAsyncAction = true; } continue; } else { const clone: Update<S, A> = { // Once we commit an optimistic update, we shouldn't uncommit it // until the transition it is associated with has finished // (represented by revertLane). Using NoLane here works because 0 // is a subset of all bitmasks, so this will never be skipped by // the check above. lane: NoLane, // Reuse the same revertLane so we know when the transition // has finished. revertLane: update.revertLane, action: update.action, hasEagerState: update.hasEagerState, eagerState: update.eagerState, next: (null: any), }; if (newBaseQueueLast === null) { newBaseQueueFirst = newBaseQueueLast = clone; newBaseState = newState; } else { newBaseQueueLast = newBaseQueueLast.next = clone; } // Update the remaining priority in the queue. // TODO: Don't need to accumulate this. Instead, we can remove // renderLanes from the original lanes. currentlyRenderingFiber.lanes = mergeLanes( currentlyRenderingFiber.lanes, revertLane, ); markSkippedUpdateLanes(revertLane); } }
// 该 update 对象的优先级足够,因此开始处理它的 action,收集新的 state 状态 const action = update.action; if (shouldDoubleInvokeUserFnsInHooksDEV) { reducer(newState, action); } if (update.hasEagerState) { // If this update is a state update (not a reducer) and was processed eagerly, // we can use the eagerly computed state newState = ((update.eagerState: any): S); } else { // ! 计算useReducer的新的state newState = reducer(newState, action); } } // 循环 baseQueue(实际上就是 pendingQueue),处理该队列中的每个 update 对象, // 并把对应的 action 操作得到的结果更新到 newState 中(即收集新状态) update = update.next; } while (update !== null && update !== first);
// Mark that the fiber performed work, but only if the new state is // different from the current state. if (!is(newState, hook.memoizedState)) { markWorkInProgressReceivedUpdate();
if (baseQueue === null) { // `queue.lanes` is used for entangling transitions. We can set it back to // zero once the queue is empty. queue.lanes = NoLanes; }
functionenqueueRenderPhaseUpdate<S, A>( queue: UpdateQueue<S, A>, update: Update<S, A>, ): void{ // This is a render phase update. Stash it in a lazily-created map of // queue -> linked list of updates. After this render pass, we'll restart // and apply the stashed updates on top of the work-in-progress hook. didScheduleRenderPhaseUpdateDuringThisPass = didScheduleRenderPhaseUpdate = true; const pending = queue.pending; if (pending === null) { // This is the first update. Create a circular list. update.next = update; } else { update.next = pending.next; pending.next = update; } queue.pending = update; }
// Check if there was a render phase update // enqueueRenderPhaseUpdate会给didScheduleRenderPhaseUpdateDuringThisPass设置为true,所以下面还会render一次,这也就是setState嵌套setState的情况下render连续 2次 if (didScheduleRenderPhaseUpdateDuringThisPass) { // Keep rendering until the component stabilizes (there are no more render // phase updates). children = renderWithHooksAgain( workInProgress, Component, props, secondArg, ); }