diff --git a/packages/react-reconciler/src/ReactFiberCommitWork.new.js b/packages/react-reconciler/src/ReactFiberCommitWork.new.js index 6d3b0e555a64f..0e9e2236b8771 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, @@ -2614,15 +2615,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; @@ -2631,7 +2639,12 @@ function commitPassiveMountEffects_begin( ensureCorrectReturnPointer(firstChild, fiber); nextEffect = firstChild; } else { - commitPassiveMountEffects_complete(subtreeRoot, root, committedLanes); + commitPassiveMountEffects_complete( + subtreeRoot, + root, + committedLanes, + committedTransitions, + ); } } } @@ -2640,6 +2653,7 @@ function commitPassiveMountEffects_complete( subtreeRoot: Fiber, root: FiberRoot, committedLanes: Lanes, + committedTransitions: Array | null, ) { while (nextEffect !== null) { const fiber = nextEffect; @@ -2647,7 +2661,12 @@ function commitPassiveMountEffects_complete( if ((fiber.flags & Passive) !== NoFlags) { setCurrentDebugFiberInDEV(fiber); try { - commitPassiveMountOnFiber(root, fiber, committedLanes); + commitPassiveMountOnFiber( + root, + fiber, + committedLanes, + committedTransitions, + ); } catch (error) { reportUncaughtErrorInDEV(error); captureCommitPhaseError(fiber, fiber.return, error); @@ -2675,6 +2694,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 9a8ba5bcd6d57..fe404d8cc8e1b 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, @@ -2614,15 +2615,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; @@ -2631,7 +2639,12 @@ function commitPassiveMountEffects_begin( ensureCorrectReturnPointer(firstChild, fiber); nextEffect = firstChild; } else { - commitPassiveMountEffects_complete(subtreeRoot, root, committedLanes); + commitPassiveMountEffects_complete( + subtreeRoot, + root, + committedLanes, + committedTransitions, + ); } } } @@ -2640,6 +2653,7 @@ function commitPassiveMountEffects_complete( subtreeRoot: Fiber, root: FiberRoot, committedLanes: Lanes, + committedTransitions: Array | null, ) { while (nextEffect !== null) { const fiber = nextEffect; @@ -2647,7 +2661,12 @@ function commitPassiveMountEffects_complete( if ((fiber.flags & Passive) !== NoFlags) { setCurrentDebugFiberInDEV(fiber); try { - commitPassiveMountOnFiber(root, fiber, committedLanes); + commitPassiveMountOnFiber( + root, + fiber, + committedLanes, + committedTransitions, + ); } catch (error) { reportUncaughtErrorInDEV(error); captureCommitPhaseError(fiber, fiber.return, error); @@ -2675,6 +2694,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 bbd4acdf9e9be..7236e0a5029d6 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.new.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.new.js @@ -391,6 +391,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; @@ -1074,7 +1075,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: { @@ -1114,14 +1119,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: { @@ -1152,7 +1166,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; @@ -1160,12 +1179,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: { @@ -1289,7 +1316,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. @@ -1971,7 +2002,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(); @@ -1980,7 +2015,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); @@ -1992,6 +2032,7 @@ function commitRoot(root: FiberRoot, recoverableErrors: null | Array) { function commitRootImpl( root: FiberRoot, recoverableErrors: null | Array, + transitions: Array | null, renderPriorityLevel: EventPriority, ) { do { @@ -2087,6 +2128,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 @@ -2407,6 +2455,10 @@ function flushPassiveEffectsImpl() { return false; } + // Cache and clear the transitions flag + const transitions = pendingPassiveTransitions; + pendingPassiveTransitions = null; + const root = rootWithPendingPassiveEffects; const lanes = pendingPassiveEffectsLanes; rootWithPendingPassiveEffects = null; @@ -2436,7 +2488,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 8e2dd32c9f166..ba575b76b6ba7 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.old.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.old.js @@ -391,6 +391,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; @@ -1074,7 +1075,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: { @@ -1114,14 +1119,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: { @@ -1152,7 +1166,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; @@ -1160,12 +1179,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: { @@ -1289,7 +1316,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. @@ -1971,7 +2002,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(); @@ -1980,7 +2015,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); @@ -1992,6 +2032,7 @@ function commitRoot(root: FiberRoot, recoverableErrors: null | Array) { function commitRootImpl( root: FiberRoot, recoverableErrors: null | Array, + transitions: Array | null, renderPriorityLevel: EventPriority, ) { do { @@ -2087,6 +2128,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 @@ -2407,6 +2455,10 @@ function flushPassiveEffectsImpl() { return false; } + // Cache and clear the transitions flag + const transitions = pendingPassiveTransitions; + pendingPassiveTransitions = null; + const root = rootWithPendingPassiveEffects; const lanes = pendingPassiveEffectsLanes; rootWithPendingPassiveEffects = null; @@ -2436,7 +2488,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) {