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) {