@samitouri / QOS-React-2 / commits / b0f13e5d39

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.

Luna Ruan committed Apr 8, 2022 at 18:35 UTC b0f13e5d391c0deba4327eb15c07e7321f3a871e
4 files changed +170 -26
packages/react-reconciler/src/ReactFiberCommitWork.new.js
+23 -3
@@ -26,6 +26,7 @@ import type {OffscreenState} from './ReactFiberOffscreenComponent';
26 import type {HookFlags} from './ReactHookEffectTags';
27 import type {Cache} from './ReactFiberCacheComponent.new';
28 import type {RootState} from './ReactFiberRoot.new';
29 +import type {Transition} from './ReactFiberTracingMarkerComponent.new';
30
31 import {
32 enableCreateEventHandleAPI,
@@ -2602,15 +2603,22 @@ export function commitPassiveMountEffects(
2603 root: FiberRoot,
2604 finishedWork: Fiber,
2605 committedLanes: Lanes,
2606 + committedTransitions: Array<Transition> | null,
2607 ): void {
2608 nextEffect = finishedWork;
2607 - commitPassiveMountEffects_begin(finishedWork, root, committedLanes);
2609 + commitPassiveMountEffects_begin(
2610 + finishedWork,
2611 + root,
2612 + committedLanes,
2613 + committedTransitions,
2614 + );
2615 }
2616
2617 function commitPassiveMountEffects_begin(
2618 subtreeRoot: Fiber,
2619 root: FiberRoot,
2620 committedLanes: Lanes,
2621 + committedTransitions: Array<Transition> | null,
2622 ) {
2623 while (nextEffect !== null) {
2624 const fiber = nextEffect;
@@ -2619,7 +2627,12 @@ function commitPassiveMountEffects_begin(
2627 firstChild.return = fiber;
2628 nextEffect = firstChild;
2629 } else {
2622 - commitPassiveMountEffects_complete(subtreeRoot, root, committedLanes);
2630 + commitPassiveMountEffects_complete(
2631 + subtreeRoot,
2632 + root,
2633 + committedLanes,
2634 + committedTransitions,
2635 + );
2636 }
2637 }
2638 }
@@ -2628,6 +2641,7 @@ function commitPassiveMountEffects_complete(
2641 subtreeRoot: Fiber,
2642 root: FiberRoot,
2643 committedLanes: Lanes,
2644 + committedTransitions: Array<Transition> | null,
2645 ) {
2646 while (nextEffect !== null) {
2647 const fiber = nextEffect;
@@ -2635,7 +2649,12 @@ function commitPassiveMountEffects_complete(
2649 if ((fiber.flags & Passive) !== NoFlags) {
2650 setCurrentDebugFiberInDEV(fiber);
2651 try {
2638 - commitPassiveMountOnFiber(root, fiber, committedLanes);
2652 + commitPassiveMountOnFiber(
2653 + root,
2654 + fiber,
2655 + committedLanes,
2656 + committedTransitions,
2657 + );
2658 } catch (error) {
2659 captureCommitPhaseError(fiber, fiber.return, error);
2660 }
@@ -2662,6 +2681,7 @@ function commitPassiveMountOnFiber(
2681 finishedRoot: FiberRoot,
2682 finishedWork: Fiber,
2683 committedLanes: Lanes,
2684 + committedTransitions: Array<Transition> | null,
2685 ): void {
2686 switch (finishedWork.tag) {
2687 case FunctionComponent:
packages/react-reconciler/src/ReactFiberCommitWork.old.js
+23 -3
@@ -26,6 +26,7 @@ import type {OffscreenState} from './ReactFiberOffscreenComponent';
26 import type {HookFlags} from './ReactHookEffectTags';
27 import type {Cache} from './ReactFiberCacheComponent.old';
28 import type {RootState} from './ReactFiberRoot.old';
29 +import type {Transition} from './ReactFiberTracingMarkerComponent.old';
30
31 import {
32 enableCreateEventHandleAPI,
@@ -2602,15 +2603,22 @@ export function commitPassiveMountEffects(
2603 root: FiberRoot,
2604 finishedWork: Fiber,
2605 committedLanes: Lanes,
2606 + committedTransitions: Array<Transition> | null,
2607 ): void {
2608 nextEffect = finishedWork;
2607 - commitPassiveMountEffects_begin(finishedWork, root, committedLanes);
2609 + commitPassiveMountEffects_begin(
2610 + finishedWork,
2611 + root,
2612 + committedLanes,
2613 + committedTransitions,
2614 + );
2615 }
2616
2617 function commitPassiveMountEffects_begin(
2618 subtreeRoot: Fiber,
2619 root: FiberRoot,
2620 committedLanes: Lanes,
2621 + committedTransitions: Array<Transition> | null,
2622 ) {
2623 while (nextEffect !== null) {
2624 const fiber = nextEffect;
@@ -2619,7 +2627,12 @@ function commitPassiveMountEffects_begin(
2627 firstChild.return = fiber;
2628 nextEffect = firstChild;
2629 } else {
2622 - commitPassiveMountEffects_complete(subtreeRoot, root, committedLanes);
2630 + commitPassiveMountEffects_complete(
2631 + subtreeRoot,
2632 + root,
2633 + committedLanes,
2634 + committedTransitions,
2635 + );
2636 }
2637 }
2638 }
@@ -2628,6 +2641,7 @@ function commitPassiveMountEffects_complete(
2641 subtreeRoot: Fiber,
2642 root: FiberRoot,
2643 committedLanes: Lanes,
2644 + committedTransitions: Array<Transition> | null,
2645 ) {
2646 while (nextEffect !== null) {
2647 const fiber = nextEffect;
@@ -2635,7 +2649,12 @@ function commitPassiveMountEffects_complete(
2649 if ((fiber.flags & Passive) !== NoFlags) {
2650 setCurrentDebugFiberInDEV(fiber);
2651 try {
2638 - commitPassiveMountOnFiber(root, fiber, committedLanes);
2652 + commitPassiveMountOnFiber(
2653 + root,
2654 + fiber,
2655 + committedLanes,
2656 + committedTransitions,
2657 + );
2658 } catch (error) {
2659 captureCommitPhaseError(fiber, fiber.return, error);
2660 }
@@ -2662,6 +2681,7 @@ function commitPassiveMountOnFiber(
2681 finishedRoot: FiberRoot,
2682 finishedWork: Fiber,
2683 committedLanes: Lanes,
2684 + committedTransitions: Array<Transition> | null,
2685 ): void {
2686 switch (finishedWork.tag) {
2687 case FunctionComponent:
packages/react-reconciler/src/ReactFiberWorkLoop.new.js
+62 -10
@@ -392,6 +392,7 @@ let rootWithPendingPassiveEffects: FiberRoot | null = null;
392 let pendingPassiveEffectsLanes: Lanes = NoLanes;
393 let pendingPassiveProfilerEffects: Array<Fiber> = [];
394 let pendingPassiveEffectsRemainingLanes: Lanes = NoLanes;
395 +let pendingPassiveTransitions: Array<Transition> | null = null;
396
397 // Use these to prevent an infinite loop of nested updates
398 const NESTED_UPDATE_LIMIT = 50;
@@ -1075,7 +1076,11 @@ function finishConcurrentRender(root, exitStatus, lanes) {
1076 case RootErrored: {
1077 // We should have already attempted to retry this tree. If we reached
1078 // this point, it errored again. Commit it.
1078 - commitRoot(root, workInProgressRootRecoverableErrors);
1079 + commitRoot(
1080 + root,
1081 + workInProgressRootRecoverableErrors,
1082 + workInProgressTransitions,
1083 + );
1084 break;
1085 }
1086 case RootSuspended: {
@@ -1115,14 +1120,23 @@ function finishConcurrentRender(root, exitStatus, lanes) {
1120 // lower priority work to do. Instead of committing the fallback
1121 // immediately, wait for more data to arrive.
1122 root.timeoutHandle = scheduleTimeout(
1118 - commitRoot.bind(null, root, workInProgressRootRecoverableErrors),
1123 + commitRoot.bind(
1124 + null,
1125 + root,
1126 + workInProgressRootRecoverableErrors,
1127 + workInProgressTransitions,
1128 + ),
1129 msUntilTimeout,
1130 );
1131 break;
1132 }
1133 }
1134 // The work expired. Commit immediately.
1125 - commitRoot(root, workInProgressRootRecoverableErrors);
1135 + commitRoot(
1136 + root,
1137 + workInProgressRootRecoverableErrors,
1138 + workInProgressTransitions,
1139 + );
1140 break;
1141 }
1142 case RootSuspendedWithDelay: {
@@ -1153,7 +1167,12 @@ function finishConcurrentRender(root, exitStatus, lanes) {
1167 // Instead of committing the fallback immediately, wait for more data
1168 // to arrive.
1169 root.timeoutHandle = scheduleTimeout(
1156 - commitRoot.bind(null, root, workInProgressRootRecoverableErrors),
1170 + commitRoot.bind(
1171 + null,
1172 + root,
1173 + workInProgressRootRecoverableErrors,
1174 + workInProgressTransitions,
1175 + ),
1176 msUntilTimeout,
1177 );
1178 break;
@@ -1161,12 +1180,20 @@ function finishConcurrentRender(root, exitStatus, lanes) {
1180 }
1181
1182 // Commit the placeholder.
1164 - commitRoot(root, workInProgressRootRecoverableErrors);
1183 + commitRoot(
1184 + root,
1185 + workInProgressRootRecoverableErrors,
1186 + workInProgressTransitions,
1187 + );
1188 break;
1189 }
1190 case RootCompleted: {
1191 // The work completed. Ready to commit.
1169 - commitRoot(root, workInProgressRootRecoverableErrors);
1192 + commitRoot(
1193 + root,
1194 + workInProgressRootRecoverableErrors,
1195 + workInProgressTransitions,
1196 + );
1197 break;
1198 }
1199 default: {
@@ -1290,7 +1317,11 @@ function performSyncWorkOnRoot(root) {
1317 const finishedWork: Fiber = (root.current.alternate: any);
1318 root.finishedWork = finishedWork;
1319 root.finishedLanes = lanes;
1293 - commitRoot(root, workInProgressRootRecoverableErrors);
1320 + commitRoot(
1321 + root,
1322 + workInProgressRootRecoverableErrors,
1323 + workInProgressTransitions,
1324 + );
1325
1326 // Before exiting, make sure there's a callback scheduled for the next
1327 // pending level.
@@ -1972,7 +2003,11 @@ function completeUnitOfWork(unitOfWork: Fiber): void {
2003 }
2004 }
2005
1975 -function commitRoot(root: FiberRoot, recoverableErrors: null | Array<mixed>) {
2006 +function commitRoot(
2007 + root: FiberRoot,
2008 + recoverableErrors: null | Array<mixed>,
2009 + transitions: Array<Transition> | null,
2010 +) {
2011 // TODO: This no longer makes any sense. We already wrap the mutation and
2012 // layout phases. Should be able to remove.
2013 const previousUpdateLanePriority = getCurrentUpdatePriority();
@@ -1981,7 +2016,12 @@ function commitRoot(root: FiberRoot, recoverableErrors: null | Array<mixed>) {
2016 try {
2017 ReactCurrentBatchConfig.transition = null;
2018 setCurrentUpdatePriority(DiscreteEventPriority);
1984 - commitRootImpl(root, recoverableErrors, previousUpdateLanePriority);
2019 + commitRootImpl(
2020 + root,
2021 + recoverableErrors,
2022 + transitions,
2023 + previousUpdateLanePriority,
2024 + );
2025 } finally {
2026 ReactCurrentBatchConfig.transition = prevTransition;
2027 setCurrentUpdatePriority(previousUpdateLanePriority);
@@ -1993,6 +2033,7 @@ function commitRoot(root: FiberRoot, recoverableErrors: null | Array<mixed>) {
2033 function commitRootImpl(
2034 root: FiberRoot,
2035 recoverableErrors: null | Array<mixed>,
2036 + transitions: Array<Transition> | null,
2037 renderPriorityLevel: EventPriority,
2038 ) {
2039 do {
@@ -2088,6 +2129,13 @@ function commitRootImpl(
2129 if (!rootDoesHavePassiveEffects) {
2130 rootDoesHavePassiveEffects = true;
2131 pendingPassiveEffectsRemainingLanes = remainingLanes;
2132 + // workInProgressTransitions might be overwritten, so we want
2133 + // to store it in pendingPassiveTransitions until they get processed
2134 + // We need to pass this through as an argument to commitRoot
2135 + // because workInProgressTransitions might have changed between
2136 + // the previous render and commit if we throttle the commit
2137 + // with setTimeout
2138 + pendingPassiveTransitions = transitions;
2139 scheduleCallback(NormalSchedulerPriority, () => {
2140 flushPassiveEffects();
2141 // This render triggered passive effects: release the root cache pool
@@ -2408,6 +2456,10 @@ function flushPassiveEffectsImpl() {
2456 return false;
2457 }
2458
2459 + // Cache and clear the transitions flag
2460 + const transitions = pendingPassiveTransitions;
2461 + pendingPassiveTransitions = null;
2462 +
2463 const root = rootWithPendingPassiveEffects;
2464 const lanes = pendingPassiveEffectsLanes;
2465 rootWithPendingPassiveEffects = null;
@@ -2437,7 +2489,7 @@ function flushPassiveEffectsImpl() {
2489 executionContext |= CommitContext;
2490
2491 commitPassiveUnmountEffects(root.current);
2440 - commitPassiveMountEffects(root, root.current, lanes);
2492 + commitPassiveMountEffects(root, root.current, lanes, transitions);
2493
2494 // TODO: Move to commitPassiveMountEffects
2495 if (enableProfilerTimer && enableProfilerCommitHooks) {
packages/react-reconciler/src/ReactFiberWorkLoop.old.js
+62 -10
@@ -392,6 +392,7 @@ let rootWithPendingPassiveEffects: FiberRoot | null = null;
392 let pendingPassiveEffectsLanes: Lanes = NoLanes;
393 let pendingPassiveProfilerEffects: Array<Fiber> = [];
394 let pendingPassiveEffectsRemainingLanes: Lanes = NoLanes;
395 +let pendingPassiveTransitions: Array<Transition> | null = null;
396
397 // Use these to prevent an infinite loop of nested updates
398 const NESTED_UPDATE_LIMIT = 50;
@@ -1075,7 +1076,11 @@ function finishConcurrentRender(root, exitStatus, lanes) {
1076 case RootErrored: {
1077 // We should have already attempted to retry this tree. If we reached
1078 // this point, it errored again. Commit it.
1078 - commitRoot(root, workInProgressRootRecoverableErrors);
1079 + commitRoot(
1080 + root,
1081 + workInProgressRootRecoverableErrors,
1082 + workInProgressTransitions,
1083 + );
1084 break;
1085 }
1086 case RootSuspended: {
@@ -1115,14 +1120,23 @@ function finishConcurrentRender(root, exitStatus, lanes) {
1120 // lower priority work to do. Instead of committing the fallback
1121 // immediately, wait for more data to arrive.
1122 root.timeoutHandle = scheduleTimeout(
1118 - commitRoot.bind(null, root, workInProgressRootRecoverableErrors),
1123 + commitRoot.bind(
1124 + null,
1125 + root,
1126 + workInProgressRootRecoverableErrors,
1127 + workInProgressTransitions,
1128 + ),
1129 msUntilTimeout,
1130 );
1131 break;
1132 }
1133 }
1134 // The work expired. Commit immediately.
1125 - commitRoot(root, workInProgressRootRecoverableErrors);
1135 + commitRoot(
1136 + root,
1137 + workInProgressRootRecoverableErrors,
1138 + workInProgressTransitions,
1139 + );
1140 break;
1141 }
1142 case RootSuspendedWithDelay: {
@@ -1153,7 +1167,12 @@ function finishConcurrentRender(root, exitStatus, lanes) {
1167 // Instead of committing the fallback immediately, wait for more data
1168 // to arrive.
1169 root.timeoutHandle = scheduleTimeout(
1156 - commitRoot.bind(null, root, workInProgressRootRecoverableErrors),
1170 + commitRoot.bind(
1171 + null,
1172 + root,
1173 + workInProgressRootRecoverableErrors,
1174 + workInProgressTransitions,
1175 + ),
1176 msUntilTimeout,
1177 );
1178 break;
@@ -1161,12 +1180,20 @@ function finishConcurrentRender(root, exitStatus, lanes) {
1180 }
1181
1182 // Commit the placeholder.
1164 - commitRoot(root, workInProgressRootRecoverableErrors);
1183 + commitRoot(
1184 + root,
1185 + workInProgressRootRecoverableErrors,
1186 + workInProgressTransitions,
1187 + );
1188 break;
1189 }
1190 case RootCompleted: {
1191 // The work completed. Ready to commit.
1169 - commitRoot(root, workInProgressRootRecoverableErrors);
1192 + commitRoot(
1193 + root,
1194 + workInProgressRootRecoverableErrors,
1195 + workInProgressTransitions,
1196 + );
1197 break;
1198 }
1199 default: {
@@ -1290,7 +1317,11 @@ function performSyncWorkOnRoot(root) {
1317 const finishedWork: Fiber = (root.current.alternate: any);
1318 root.finishedWork = finishedWork;
1319 root.finishedLanes = lanes;
1293 - commitRoot(root, workInProgressRootRecoverableErrors);
1320 + commitRoot(
1321 + root,
1322 + workInProgressRootRecoverableErrors,
1323 + workInProgressTransitions,
1324 + );
1325
1326 // Before exiting, make sure there's a callback scheduled for the next
1327 // pending level.
@@ -1972,7 +2003,11 @@ function completeUnitOfWork(unitOfWork: Fiber): void {
2003 }
2004 }
2005
1975 -function commitRoot(root: FiberRoot, recoverableErrors: null | Array<mixed>) {
2006 +function commitRoot(
2007 + root: FiberRoot,
2008 + recoverableErrors: null | Array<mixed>,
2009 + transitions: Array<Transition> | null,
2010 +) {
2011 // TODO: This no longer makes any sense. We already wrap the mutation and
2012 // layout phases. Should be able to remove.
2013 const previousUpdateLanePriority = getCurrentUpdatePriority();
@@ -1981,7 +2016,12 @@ function commitRoot(root: FiberRoot, recoverableErrors: null | Array<mixed>) {
2016 try {
2017 ReactCurrentBatchConfig.transition = null;
2018 setCurrentUpdatePriority(DiscreteEventPriority);
1984 - commitRootImpl(root, recoverableErrors, previousUpdateLanePriority);
2019 + commitRootImpl(
2020 + root,
2021 + recoverableErrors,
2022 + transitions,
2023 + previousUpdateLanePriority,
2024 + );
2025 } finally {
2026 ReactCurrentBatchConfig.transition = prevTransition;
2027 setCurrentUpdatePriority(previousUpdateLanePriority);
@@ -1993,6 +2033,7 @@ function commitRoot(root: FiberRoot, recoverableErrors: null | Array<mixed>) {
2033 function commitRootImpl(
2034 root: FiberRoot,
2035 recoverableErrors: null | Array<mixed>,
2036 + transitions: Array<Transition> | null,
2037 renderPriorityLevel: EventPriority,
2038 ) {
2039 do {
@@ -2088,6 +2129,13 @@ function commitRootImpl(
2129 if (!rootDoesHavePassiveEffects) {
2130 rootDoesHavePassiveEffects = true;
2131 pendingPassiveEffectsRemainingLanes = remainingLanes;
2132 + // workInProgressTransitions might be overwritten, so we want
2133 + // to store it in pendingPassiveTransitions until they get processed
2134 + // We need to pass this through as an argument to commitRoot
2135 + // because workInProgressTransitions might have changed between
2136 + // the previous render and commit if we throttle the commit
2137 + // with setTimeout
2138 + pendingPassiveTransitions = transitions;
2139 scheduleCallback(NormalSchedulerPriority, () => {
2140 flushPassiveEffects();
2141 // This render triggered passive effects: release the root cache pool
@@ -2408,6 +2456,10 @@ function flushPassiveEffectsImpl() {
2456 return false;
2457 }
2458
2459 + // Cache and clear the transitions flag
2460 + const transitions = pendingPassiveTransitions;
2461 + pendingPassiveTransitions = null;
2462 +
2463 const root = rootWithPendingPassiveEffects;
2464 const lanes = pendingPassiveEffectsLanes;
2465 rootWithPendingPassiveEffects = null;
@@ -2437,7 +2489,7 @@ function flushPassiveEffectsImpl() {
2489 executionContext |= CommitContext;
2490
2491 commitPassiveUnmountEffects(root.current);
2440 - commitPassiveMountEffects(root, root.current, lanes);
2492 + commitPassiveMountEffects(root, root.current, lanes, transitions);
2493
2494 // TODO: Move to commitPassiveMountEffects
2495 if (enableProfilerTimer && enableProfilerCommitHooks) {