export function enqueueUpdate<State>( fiber: Fiber, update: Update<State>, lane: Lane, ) { const updateQueue = fiber.updateQueue; if (updateQueue === null) { // 只在fiber 卸载的时候会发生 // Only occurs if the fiber has been unmounted. return; }
const sharedQueue: SharedQueue<State> = (updateQueue: any).shared; // 比较fiber lane和lane,相同时更新,具体内容可以看下面的源码。这里不加讨论 // render初始化时不执行 if (isInterleavedUpdate(fiber, lane)) { const interleaved = sharedQueue.interleaved; if (interleaved === null) { // 如果是第一次更新,创建双向链表 // This is the first update. Create a circular list. update.next = update; //在当前渲染结束时,将显示此队列的交错更新 //被转移到挂起队列。 // At the end of the current render, this queue's interleaved updates will // be transfered to the pending queue. pushInterleavedQueue(sharedQueue); } else { update.next = interleaved.next; interleaved.next = update; } sharedQueue.interleaved = update; } else { 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; } // 开发模式 if (__DEV__) { if ( currentlyProcessingQueue === sharedQueue && !didWarnUpdateInsideUpdate ) { 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.', ); didWarnUpdateInsideUpdate = true; } } }
exportfunctionisInterleavedUpdate(fiber: Fiber, lane: Lane) { return ( // TODO: Optimize slightly by comparing to root that fiber belongs to. // Requires some refactoring. Not a big deal though since it's rare for // concurrent apps to have more than a single root. //TODO:通过与fiber所属的根进行比较来稍微优化。 //需要一些重构。不过没什么大不了的,因为它很少见 //具有多个根的并发应用程序。 workInProgressRoot !== null && (fiber.mode & ConcurrentMode) !== NoMode && // If this is a render phase update (i.e. UNSAFE_componentWillReceiveProps), // then don't treat this as an interleaved update. This pattern is // accompanied by a warning but we haven't fully deprecated it yet. We can // remove once the deferRenderPhaseUpdateToNextBatch flag is enabled. //如果这是渲染阶段更新(即 UNSAFE_componentWillReceiveProps), //然后不要将其视为交错更新。这个图案是 //伴随着警告,但我们还没有完全弃用它。我们可以 //启用 deferRenderPhaseUpdateToNextBatch 标志后删除。 (deferRenderPhaseUpdateToNextBatch || (executionContext & RenderContext) === NoContext) ); }
// 判断是否有在跟踪 if (enableUpdaterTracking) { // 是否是开发环境 if (isDevToolsPresent) { // 添加Fiber的Map关系 addFiberToLanesMap(root, fiber, lane); } }
// Mark that the root has a pending update. // 在root上标记更新,将update的lane放到root.pendingLanes markRootUpdated(root, lane, eventTime);
if (enableProfilerTimer && enableProfilerNestedUpdateScheduledHook) { if ( (executionContext & CommitContext) !== NoContext && root === rootCommittingMutationOrLayoutEffects ) { if (fiber.mode & ProfileMode) { let current = fiber; while (current !== null) { if (current.tag === Profiler) { const {id, onNestedUpdateScheduled} = current.memoizedProps; if (typeof onNestedUpdateScheduled === 'function') { onNestedUpdateScheduled(id); } } current = current.return; } } } }
// TODO: Consolidate with `isInterleavedUpdate` check if (root === workInProgressRoot) { // Received an update to a tree that's in the middle of rendering. Mark // that there was an interleaved update work on this root. Unless the // `deferRenderPhaseUpdateToNextBatch` flag is off and this is a render // phase update. In that case, we don't treat render phase updates as if // they were interleaved, for backwards compat reasons. if ( deferRenderPhaseUpdateToNextBatch || (executionContext & RenderContext) === NoContext ) { workInProgressRootUpdatedLanes = mergeLanes( workInProgressRootUpdatedLanes, lane, ); } if (workInProgressRootExitStatus === RootSuspendedWithDelay) { // The root already suspended with a delay, which means this render // definitely won't finish. Since we have a new update, let's mark it as // suspended now, right before marking the incoming update. This has the // effect of interrupting the current render and switching to the update. // TODO: Make sure this doesn't override pings that happen while we've // already started rendering. markRootSuspended(root, workInProgressRootRenderLanes); } }
if (lane === SyncLane) { if ( // Check if we're inside unbatchedUpdates (executionContext & LegacyUnbatchedContext) !== NoContext && // Check if we're not already rendering (executionContext & (RenderContext | CommitContext)) === NoContext ) { // This is a legacy edge case. The initial mount of a ReactDOM.render-ed // root inside of batchedUpdates should be synchronous, but layout updates // should be deferred until the end of the batch. performSyncWorkOnRoot(root); } else { ensureRootIsScheduled(root, eventTime); if ( executionContext === NoContext && (fiber.mode & ConcurrentMode) === NoMode ) { // Flush the synchronous work now, unless we're already working or inside // a batch. This is intentionally inside scheduleUpdateOnFiber instead of // scheduleCallbackForFiber to preserve the ability to schedule a callback // without immediately flushing it. We only do this for user-initiated // updates, to preserve historical behavior of legacy mode. resetRenderTimer(); flushSyncCallbacksOnlyInLegacyMode(); } } } else { // Schedule other updates after in case the callback is sync. ensureRootIsScheduled(root, eventTime); }
functioncheckForNestedUpdates() { if (nestedUpdateCount > NESTED_UPDATE_LIMIT) { nestedUpdateCount = 0; rootWithNestedUpdates = null; invariant( false, 'Maximum update depth exceeded. This can happen when a component ' + 'repeatedly calls setState inside componentWillUpdate or ' + 'componentDidUpdate. React limits the number of nested updates to ' + 'prevent infinite loops.', ); }
if (__DEV__) { if (nestedPassiveUpdateCount > NESTED_PASSIVE_UPDATE_LIMIT) { nestedPassiveUpdateCount = 0; console.error( 'Maximum update depth exceeded. This can happen when a component ' + "calls setState inside useEffect, but useEffect either doesn't " + 'have a dependency array, or one of the dependencies changes on ' + 'every render.', ); } } }