From 23d5eec7f558906ab9f6ea740b1f40fb53bdec32 Mon Sep 17 00:00:00 2001 From: Luna Ruan Date: Fri, 8 Apr 2022 18:35:18 -0500 Subject: [PATCH] add pendingPassiveTransitions (#24320) Add pendingPassiveTransitions work loop module level variable. Because workInProgressTransitions might change before we process it in the passive effects, we introduce a new variable, pendingPassiveTransitions, where we store the transitions until we can actually process them in the commit phase. --- .../src/ReactFiberCommitWork.new.js | 26 ++++++- .../src/ReactFiberCommitWork.old.js | 26 ++++++- .../src/ReactFiberWorkLoop.new.js | 72 ++++++++++++++++--- .../src/ReactFiberWorkLoop.old.js | 72 ++++++++++++++++--- 4 files changed, 170 insertions(+), 26 deletions(-) diff --git a/packages/react-reconciler/src/ReactFiberCommitWork.new.js b/packages/react-reconciler/src/ReactFiberCommitWork.new.js index 8f7741f74ee1f..c9f40139f64e1 100644 --- a/packages/react-reconciler/src/ReactFiberCommitWork.new.js +++ b/packages/react-reconciler/src/ReactFiberCommitWork.new.js @@ -26,6 +26,7 @@ import type {OffscreenState} from './ReactFiberOffscreenComponent'; import type {HookFlags} from './ReactHookEffectTags'; import type {Cache} from './ReactFiberCacheComponent.new'; import type {RootState} from './ReactFiberRoot.new'; +import type {Transition} from './ReactFiberTracingMarkerComponent.new'; import { enableCreateEventHandleAPI, @@ -2602,15 +2603,22 @@ export function commitPassiveMountEffects( root: FiberRoot, finishedWork: Fiber, committedLanes: Lanes, + committedTransitions: Array | null, ): void { nextEffect = finishedWork; - commitPassiveMountEffects_begin(finishedWork, root, committedLanes); + commitPassiveMountEffects_begin( + finishedWork, + root, + committedLanes, + committedTransitions, + ); } function commitPassiveMountEffects_begin( subtreeRoot: Fiber, root: FiberRoot, committedLanes: Lanes, + committedTransitions: Array | null, ) { while (nextEffect !== null) { const fiber = nextEffect; @@ -2619,7 +2627,12 @@ function commitPassiveMountEffects_begin( firstChild.return = fiber; nextEffect = firstChild; } else { - commitPassiveMountEffects_complete(subtreeRoot, root, committedLanes); + commitPassiveMountEffects_complete( + subtreeRoot, + root, + committedLanes, + committedTransitions, + ); } } } @@ -2628,6 +2641,7 @@ function commitPassiveMountEffects_complete( subtreeRoot: Fiber, root: FiberRoot, committedLanes: Lanes, + committedTransitions: Array | null, ) { while (nextEffect !== null) { const fiber = nextEffect; @@ -2635,7 +2649,12 @@ function commitPassiveMountEffects_complete( if ((fiber.flags & Passive) !== NoFlags) { setCurrentDebugFiberInDEV(fiber); try { - commitPassiveMountOnFiber(root, fiber, committedLanes); + commitPassiveMountOnFiber( + root, + fiber, + committedLanes, + committedTransitions, + ); } catch (error) { captureCommitPhaseError(fiber, fiber.return, error); } @@ -2662,6 +2681,7 @@ function commitPassiveMountOnFiber( finishedRoot: FiberRoot, finishedWork: Fiber, committedLanes: Lanes, + committedTransitions: Array | null, ): void { switch (finishedWork.tag) { case FunctionComponent: diff --git a/packages/react-reconciler/src/ReactFiberCommitWork.old.js b/packages/react-reconciler/src/ReactFiberCommitWork.old.js index 70b416b3a3224..731eaac9ef9ad 100644 --- a/packages/react-reconciler/src/ReactFiberCommitWork.old.js +++ b/packages/react-reconciler/src/ReactFiberCommitWork.old.js @@ -26,6 +26,7 @@ import type {OffscreenState} from './ReactFiberOffscreenComponent'; import type {HookFlags} from './ReactHookEffectTags'; import type {Cache} from './ReactFiberCacheComponent.old'; import type {RootState} from './ReactFiberRoot.old'; +import type {Transition} from './ReactFiberTracingMarkerComponent.old'; import { enableCreateEventHandleAPI, @@ -2602,15 +2603,22 @@ export function commitPassiveMountEffects( root: FiberRoot, finishedWork: Fiber, committedLanes: Lanes, + committedTransitions: Array | null, ): void { nextEffect = finishedWork; - commitPassiveMountEffects_begin(finishedWork, root, committedLanes); + commitPassiveMountEffects_begin( + finishedWork, + root, + committedLanes, + committedTransitions, + ); } function commitPassiveMountEffects_begin( subtreeRoot: Fiber, root: FiberRoot, committedLanes: Lanes, + committedTransitions: Array | null, ) { while (nextEffect !== null) { const fiber = nextEffect; @@ -2619,7 +2627,12 @@ function commitPassiveMountEffects_begin( firstChild.return = fiber; nextEffect = firstChild; } else { - commitPassiveMountEffects_complete(subtreeRoot, root, committedLanes); + commitPassiveMountEffects_complete( + subtreeRoot, + root, + committedLanes, + committedTransitions, + ); } } } @@ -2628,6 +2641,7 @@ function commitPassiveMountEffects_complete( subtreeRoot: Fiber, root: FiberRoot, committedLanes: Lanes, + committedTransitions: Array | null, ) { while (nextEffect !== null) { const fiber = nextEffect; @@ -2635,7 +2649,12 @@ function commitPassiveMountEffects_complete( if ((fiber.flags & Passive) !== NoFlags) { setCurrentDebugFiberInDEV(fiber); try { - commitPassiveMountOnFiber(root, fiber, committedLanes); + commitPassiveMountOnFiber( + root, + fiber, + committedLanes, + committedTransitions, + ); } catch (error) { captureCommitPhaseError(fiber, fiber.return, error); } @@ -2662,6 +2681,7 @@ function commitPassiveMountOnFiber( finishedRoot: FiberRoot, finishedWork: Fiber, committedLanes: Lanes, + committedTransitions: Array | null, ): void { switch (finishedWork.tag) { case FunctionComponent: diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.new.js b/packages/react-reconciler/src/ReactFiberWorkLoop.new.js index 643724724aaaa..b6e7fad4eb944 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.new.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.new.js @@ -392,6 +392,7 @@ let rootWithPendingPassiveEffects: FiberRoot | null = null; let pendingPassiveEffectsLanes: Lanes = NoLanes; let pendingPassiveProfilerEffects: Array = []; let pendingPassiveEffectsRemainingLanes: Lanes = NoLanes; +let pendingPassiveTransitions: Array | null = null; // Use these to prevent an infinite loop of nested updates const NESTED_UPDATE_LIMIT = 50; @@ -1075,7 +1076,11 @@ function finishConcurrentRender(root, exitStatus, lanes) { case RootErrored: { // We should have already attempted to retry this tree. If we reached // this point, it errored again. Commit it. - commitRoot(root, workInProgressRootRecoverableErrors); + commitRoot( + root, + workInProgressRootRecoverableErrors, + workInProgressTransitions, + ); break; } case RootSuspended: { @@ -1115,14 +1120,23 @@ function finishConcurrentRender(root, exitStatus, lanes) { // lower priority work to do. Instead of committing the fallback // immediately, wait for more data to arrive. root.timeoutHandle = scheduleTimeout( - commitRoot.bind(null, root, workInProgressRootRecoverableErrors), + commitRoot.bind( + null, + root, + workInProgressRootRecoverableErrors, + workInProgressTransitions, + ), msUntilTimeout, ); break; } } // The work expired. Commit immediately. - commitRoot(root, workInProgressRootRecoverableErrors); + commitRoot( + root, + workInProgressRootRecoverableErrors, + workInProgressTransitions, + ); break; } case RootSuspendedWithDelay: { @@ -1153,7 +1167,12 @@ function finishConcurrentRender(root, exitStatus, lanes) { // Instead of committing the fallback immediately, wait for more data // to arrive. root.timeoutHandle = scheduleTimeout( - commitRoot.bind(null, root, workInProgressRootRecoverableErrors), + commitRoot.bind( + null, + root, + workInProgressRootRecoverableErrors, + workInProgressTransitions, + ), msUntilTimeout, ); break; @@ -1161,12 +1180,20 @@ function finishConcurrentRender(root, exitStatus, lanes) { } // Commit the placeholder. - commitRoot(root, workInProgressRootRecoverableErrors); + commitRoot( + root, + workInProgressRootRecoverableErrors, + workInProgressTransitions, + ); break; } case RootCompleted: { // The work completed. Ready to commit. - commitRoot(root, workInProgressRootRecoverableErrors); + commitRoot( + root, + workInProgressRootRecoverableErrors, + workInProgressTransitions, + ); break; } default: { @@ -1290,7 +1317,11 @@ function performSyncWorkOnRoot(root) { const finishedWork: Fiber = (root.current.alternate: any); root.finishedWork = finishedWork; root.finishedLanes = lanes; - commitRoot(root, workInProgressRootRecoverableErrors); + commitRoot( + root, + workInProgressRootRecoverableErrors, + workInProgressTransitions, + ); // Before exiting, make sure there's a callback scheduled for the next // pending level. @@ -1972,7 +2003,11 @@ function completeUnitOfWork(unitOfWork: Fiber): void { } } -function commitRoot(root: FiberRoot, recoverableErrors: null | Array) { +function commitRoot( + root: FiberRoot, + recoverableErrors: null | Array, + transitions: Array | null, +) { // TODO: This no longer makes any sense. We already wrap the mutation and // layout phases. Should be able to remove. const previousUpdateLanePriority = getCurrentUpdatePriority(); @@ -1981,7 +2016,12 @@ function commitRoot(root: FiberRoot, recoverableErrors: null | Array) { try { ReactCurrentBatchConfig.transition = null; setCurrentUpdatePriority(DiscreteEventPriority); - commitRootImpl(root, recoverableErrors, previousUpdateLanePriority); + commitRootImpl( + root, + recoverableErrors, + transitions, + previousUpdateLanePriority, + ); } finally { ReactCurrentBatchConfig.transition = prevTransition; setCurrentUpdatePriority(previousUpdateLanePriority); @@ -1993,6 +2033,7 @@ function commitRoot(root: FiberRoot, recoverableErrors: null | Array) { function commitRootImpl( root: FiberRoot, recoverableErrors: null | Array, + transitions: Array | null, renderPriorityLevel: EventPriority, ) { do { @@ -2088,6 +2129,13 @@ function commitRootImpl( if (!rootDoesHavePassiveEffects) { rootDoesHavePassiveEffects = true; pendingPassiveEffectsRemainingLanes = remainingLanes; + // workInProgressTransitions might be overwritten, so we want + // to store it in pendingPassiveTransitions until they get processed + // We need to pass this through as an argument to commitRoot + // because workInProgressTransitions might have changed between + // the previous render and commit if we throttle the commit + // with setTimeout + pendingPassiveTransitions = transitions; scheduleCallback(NormalSchedulerPriority, () => { flushPassiveEffects(); // This render triggered passive effects: release the root cache pool @@ -2408,6 +2456,10 @@ function flushPassiveEffectsImpl() { return false; } + // Cache and clear the transitions flag + const transitions = pendingPassiveTransitions; + pendingPassiveTransitions = null; + const root = rootWithPendingPassiveEffects; const lanes = pendingPassiveEffectsLanes; rootWithPendingPassiveEffects = null; @@ -2437,7 +2489,7 @@ function flushPassiveEffectsImpl() { executionContext |= CommitContext; commitPassiveUnmountEffects(root.current); - commitPassiveMountEffects(root, root.current, lanes); + commitPassiveMountEffects(root, root.current, lanes, transitions); // TODO: Move to commitPassiveMountEffects if (enableProfilerTimer && enableProfilerCommitHooks) { diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.old.js b/packages/react-reconciler/src/ReactFiberWorkLoop.old.js index a79e2071b25a3..62714c8cd3347 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.old.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.old.js @@ -392,6 +392,7 @@ let rootWithPendingPassiveEffects: FiberRoot | null = null; let pendingPassiveEffectsLanes: Lanes = NoLanes; let pendingPassiveProfilerEffects: Array = []; let pendingPassiveEffectsRemainingLanes: Lanes = NoLanes; +let pendingPassiveTransitions: Array | null = null; // Use these to prevent an infinite loop of nested updates const NESTED_UPDATE_LIMIT = 50; @@ -1075,7 +1076,11 @@ function finishConcurrentRender(root, exitStatus, lanes) { case RootErrored: { // We should have already attempted to retry this tree. If we reached // this point, it errored again. Commit it. - commitRoot(root, workInProgressRootRecoverableErrors); + commitRoot( + root, + workInProgressRootRecoverableErrors, + workInProgressTransitions, + ); break; } case RootSuspended: { @@ -1115,14 +1120,23 @@ function finishConcurrentRender(root, exitStatus, lanes) { // lower priority work to do. Instead of committing the fallback // immediately, wait for more data to arrive. root.timeoutHandle = scheduleTimeout( - commitRoot.bind(null, root, workInProgressRootRecoverableErrors), + commitRoot.bind( + null, + root, + workInProgressRootRecoverableErrors, + workInProgressTransitions, + ), msUntilTimeout, ); break; } } // The work expired. Commit immediately. - commitRoot(root, workInProgressRootRecoverableErrors); + commitRoot( + root, + workInProgressRootRecoverableErrors, + workInProgressTransitions, + ); break; } case RootSuspendedWithDelay: { @@ -1153,7 +1167,12 @@ function finishConcurrentRender(root, exitStatus, lanes) { // Instead of committing the fallback immediately, wait for more data // to arrive. root.timeoutHandle = scheduleTimeout( - commitRoot.bind(null, root, workInProgressRootRecoverableErrors), + commitRoot.bind( + null, + root, + workInProgressRootRecoverableErrors, + workInProgressTransitions, + ), msUntilTimeout, ); break; @@ -1161,12 +1180,20 @@ function finishConcurrentRender(root, exitStatus, lanes) { } // Commit the placeholder. - commitRoot(root, workInProgressRootRecoverableErrors); + commitRoot( + root, + workInProgressRootRecoverableErrors, + workInProgressTransitions, + ); break; } case RootCompleted: { // The work completed. Ready to commit. - commitRoot(root, workInProgressRootRecoverableErrors); + commitRoot( + root, + workInProgressRootRecoverableErrors, + workInProgressTransitions, + ); break; } default: { @@ -1290,7 +1317,11 @@ function performSyncWorkOnRoot(root) { const finishedWork: Fiber = (root.current.alternate: any); root.finishedWork = finishedWork; root.finishedLanes = lanes; - commitRoot(root, workInProgressRootRecoverableErrors); + commitRoot( + root, + workInProgressRootRecoverableErrors, + workInProgressTransitions, + ); // Before exiting, make sure there's a callback scheduled for the next // pending level. @@ -1972,7 +2003,11 @@ function completeUnitOfWork(unitOfWork: Fiber): void { } } -function commitRoot(root: FiberRoot, recoverableErrors: null | Array) { +function commitRoot( + root: FiberRoot, + recoverableErrors: null | Array, + transitions: Array | null, +) { // TODO: This no longer makes any sense. We already wrap the mutation and // layout phases. Should be able to remove. const previousUpdateLanePriority = getCurrentUpdatePriority(); @@ -1981,7 +2016,12 @@ function commitRoot(root: FiberRoot, recoverableErrors: null | Array) { try { ReactCurrentBatchConfig.transition = null; setCurrentUpdatePriority(DiscreteEventPriority); - commitRootImpl(root, recoverableErrors, previousUpdateLanePriority); + commitRootImpl( + root, + recoverableErrors, + transitions, + previousUpdateLanePriority, + ); } finally { ReactCurrentBatchConfig.transition = prevTransition; setCurrentUpdatePriority(previousUpdateLanePriority); @@ -1993,6 +2033,7 @@ function commitRoot(root: FiberRoot, recoverableErrors: null | Array) { function commitRootImpl( root: FiberRoot, recoverableErrors: null | Array, + transitions: Array | null, renderPriorityLevel: EventPriority, ) { do { @@ -2088,6 +2129,13 @@ function commitRootImpl( if (!rootDoesHavePassiveEffects) { rootDoesHavePassiveEffects = true; pendingPassiveEffectsRemainingLanes = remainingLanes; + // workInProgressTransitions might be overwritten, so we want + // to store it in pendingPassiveTransitions until they get processed + // We need to pass this through as an argument to commitRoot + // because workInProgressTransitions might have changed between + // the previous render and commit if we throttle the commit + // with setTimeout + pendingPassiveTransitions = transitions; scheduleCallback(NormalSchedulerPriority, () => { flushPassiveEffects(); // This render triggered passive effects: release the root cache pool @@ -2408,6 +2456,10 @@ function flushPassiveEffectsImpl() { return false; } + // Cache and clear the transitions flag + const transitions = pendingPassiveTransitions; + pendingPassiveTransitions = null; + const root = rootWithPendingPassiveEffects; const lanes = pendingPassiveEffectsLanes; rootWithPendingPassiveEffects = null; @@ -2437,7 +2489,7 @@ function flushPassiveEffectsImpl() { executionContext |= CommitContext; commitPassiveUnmountEffects(root.current); - commitPassiveMountEffects(root, root.current, lanes); + commitPassiveMountEffects(root, root.current, lanes, transitions); // TODO: Move to commitPassiveMountEffects if (enableProfilerTimer && enableProfilerCommitHooks) {