moved mutation code to passive (#24251)
This PR moves the code for transition tracing in the mutation phase that adds transitions to the pending callbacks object (to be called sometime later after paint) from the mutation to the passive phase. Things to think about: Passive effects can be flushed before or after paint. How do we make sure that we get the correct end time for the interaction?
Luna Ruan committed
Apr 8, 2022 at 11:28 UTC
4ebaeae40dec52c84c968a25f1f21f4d0f571336
6 files changed
+176
-104
packages/react-reconciler/src/ReactFiberCommitWork.new.js
+33
-30
@@ -2227,32 +2227,6 @@ function commitMutationEffectsOnFiber(
2227
// because of the shared reconciliation logic below.
2228
const flags = finishedWork.flags;
2229
2230
- if (enableTransitionTracing) {
2231
- switch (finishedWork.tag) {
2232
- case HostRoot: {
2233
- const state = finishedWork.memoizedState;
2234
- const transitions = state.transitions;
2235
- if (transitions !== null) {
2236
- transitions.forEach(transition => {
2237
- // TODO(luna) Do we want to log TransitionStart in the startTransition callback instead?
2238
- addTransitionStartCallbackToPendingTransition({
2239
- transitionName: transition.name,
2240
- startTime: transition.startTime,
2241
- });
2242
-
2243
- addTransitionCompleteCallbackToPendingTransition({
2244
- transitionName: transition.name,
2245
- startTime: transition.startTime,
2246
- });
2247
- });
2248
-
2249
- clearTransitionsForLanes(root, lanes);
2250
- state.transitions = null;
2251
- }
2252
- }
2253
- }
2254
- }
2255
-
2230
if (flags & ContentReset) {
2231
commitResetTextContent(finishedWork);
2232
}
@@ -2639,12 +2613,17 @@ function reappearLayoutEffects_complete(subtreeRoot: Fiber) {
2613
export function commitPassiveMountEffects(
2614
root: FiberRoot,
2615
finishedWork: Fiber,
2616
+ committedLanes: Lanes,
2617
): void {
2618
nextEffect = finishedWork;
2644
- commitPassiveMountEffects_begin(finishedWork, root);
2619
+ commitPassiveMountEffects_begin(finishedWork, root, committedLanes);
2620
}
2621
2647
-function commitPassiveMountEffects_begin(subtreeRoot: Fiber, root: FiberRoot) {
2622
+function commitPassiveMountEffects_begin(
2623
+ subtreeRoot: Fiber,
2624
+ root: FiberRoot,
2625
+ committedLanes: Lanes,
2626
+) {
2627
while (nextEffect !== null) {
2628
const fiber = nextEffect;
2629
const firstChild = fiber.child;
@@ -2652,7 +2631,7 @@ function commitPassiveMountEffects_begin(subtreeRoot: Fiber, root: FiberRoot) {
2631
ensureCorrectReturnPointer(firstChild, fiber);
2632
nextEffect = firstChild;
2633
} else {
2655
- commitPassiveMountEffects_complete(subtreeRoot, root);
2634
+ commitPassiveMountEffects_complete(subtreeRoot, root, committedLanes);
2635
}
2636
}
2637
}
@@ -2660,13 +2639,15 @@ function commitPassiveMountEffects_begin(subtreeRoot: Fiber, root: FiberRoot) {
2639
function commitPassiveMountEffects_complete(
2640
subtreeRoot: Fiber,
2641
root: FiberRoot,
2642
+ committedLanes: Lanes,
2643
) {
2644
while (nextEffect !== null) {
2645
const fiber = nextEffect;
2646
+
2647
if ((fiber.flags & Passive) !== NoFlags) {
2648
setCurrentDebugFiberInDEV(fiber);
2649
try {
2669
- commitPassiveMountOnFiber(root, fiber);
2650
+ commitPassiveMountOnFiber(root, fiber, committedLanes);
2651
} catch (error) {
2652
reportUncaughtErrorInDEV(error);
2653
captureCommitPhaseError(fiber, fiber.return, error);
@@ -2693,6 +2674,7 @@ function commitPassiveMountEffects_complete(
2674
function commitPassiveMountOnFiber(
2675
finishedRoot: FiberRoot,
2676
finishedWork: Fiber,
2677
+ committedLanes: Lanes,
2678
): void {
2679
switch (finishedWork.tag) {
2680
case FunctionComponent:
@@ -2734,6 +2716,27 @@ function commitPassiveMountOnFiber(
2716
}
2717
}
2718
}
2719
+
2720
+ if (enableTransitionTracing) {
2721
+ const transitions = finishedWork.memoizedState.transitions;
2722
+ if (transitions !== null) {
2723
+ transitions.forEach(transition => {
2724
+ // TODO(luna) Do we want to log TransitionStart in the startTransition callback instead?
2725
+ addTransitionStartCallbackToPendingTransition({
2726
+ transitionName: transition.name,
2727
+ startTime: transition.startTime,
2728
+ });
2729
+
2730
+ addTransitionCompleteCallbackToPendingTransition({
2731
+ transitionName: transition.name,
2732
+ startTime: transition.startTime,
2733
+ });
2734
+ });
2735
+
2736
+ clearTransitionsForLanes(finishedRoot, committedLanes);
2737
+ finishedWork.memoizedState.transitions = null;
2738
+ }
2739
+ }
2740
break;
2741
}
2742
case LegacyHiddenComponent:
packages/react-reconciler/src/ReactFiberCommitWork.old.js
+33
-30
@@ -2227,32 +2227,6 @@ function commitMutationEffectsOnFiber(
2227
// because of the shared reconciliation logic below.
2228
const flags = finishedWork.flags;
2229
2230
- if (enableTransitionTracing) {
2231
- switch (finishedWork.tag) {
2232
- case HostRoot: {
2233
- const state = finishedWork.memoizedState;
2234
- const transitions = state.transitions;
2235
- if (transitions !== null) {
2236
- transitions.forEach(transition => {
2237
- // TODO(luna) Do we want to log TransitionStart in the startTransition callback instead?
2238
- addTransitionStartCallbackToPendingTransition({
2239
- transitionName: transition.name,
2240
- startTime: transition.startTime,
2241
- });
2242
-
2243
- addTransitionCompleteCallbackToPendingTransition({
2244
- transitionName: transition.name,
2245
- startTime: transition.startTime,
2246
- });
2247
- });
2248
-
2249
- clearTransitionsForLanes(root, lanes);
2250
- state.transitions = null;
2251
- }
2252
- }
2253
- }
2254
- }
2255
-
2230
if (flags & ContentReset) {
2231
commitResetTextContent(finishedWork);
2232
}
@@ -2639,12 +2613,17 @@ function reappearLayoutEffects_complete(subtreeRoot: Fiber) {
2613
export function commitPassiveMountEffects(
2614
root: FiberRoot,
2615
finishedWork: Fiber,
2616
+ committedLanes: Lanes,
2617
): void {
2618
nextEffect = finishedWork;
2644
- commitPassiveMountEffects_begin(finishedWork, root);
2619
+ commitPassiveMountEffects_begin(finishedWork, root, committedLanes);
2620
}
2621
2647
-function commitPassiveMountEffects_begin(subtreeRoot: Fiber, root: FiberRoot) {
2622
+function commitPassiveMountEffects_begin(
2623
+ subtreeRoot: Fiber,
2624
+ root: FiberRoot,
2625
+ committedLanes: Lanes,
2626
+) {
2627
while (nextEffect !== null) {
2628
const fiber = nextEffect;
2629
const firstChild = fiber.child;
@@ -2652,7 +2631,7 @@ function commitPassiveMountEffects_begin(subtreeRoot: Fiber, root: FiberRoot) {
2631
ensureCorrectReturnPointer(firstChild, fiber);
2632
nextEffect = firstChild;
2633
} else {
2655
- commitPassiveMountEffects_complete(subtreeRoot, root);
2634
+ commitPassiveMountEffects_complete(subtreeRoot, root, committedLanes);
2635
}
2636
}
2637
}
@@ -2660,13 +2639,15 @@ function commitPassiveMountEffects_begin(subtreeRoot: Fiber, root: FiberRoot) {
2639
function commitPassiveMountEffects_complete(
2640
subtreeRoot: Fiber,
2641
root: FiberRoot,
2642
+ committedLanes: Lanes,
2643
) {
2644
while (nextEffect !== null) {
2645
const fiber = nextEffect;
2646
+
2647
if ((fiber.flags & Passive) !== NoFlags) {
2648
setCurrentDebugFiberInDEV(fiber);
2649
try {
2669
- commitPassiveMountOnFiber(root, fiber);
2650
+ commitPassiveMountOnFiber(root, fiber, committedLanes);
2651
} catch (error) {
2652
reportUncaughtErrorInDEV(error);
2653
captureCommitPhaseError(fiber, fiber.return, error);
@@ -2693,6 +2674,7 @@ function commitPassiveMountEffects_complete(
2674
function commitPassiveMountOnFiber(
2675
finishedRoot: FiberRoot,
2676
finishedWork: Fiber,
2677
+ committedLanes: Lanes,
2678
): void {
2679
switch (finishedWork.tag) {
2680
case FunctionComponent:
@@ -2734,6 +2716,27 @@ function commitPassiveMountOnFiber(
2716
}
2717
}
2718
}
2719
+
2720
+ if (enableTransitionTracing) {
2721
+ const transitions = finishedWork.memoizedState.transitions;
2722
+ if (transitions !== null) {
2723
+ transitions.forEach(transition => {
2724
+ // TODO(luna) Do we want to log TransitionStart in the startTransition callback instead?
2725
+ addTransitionStartCallbackToPendingTransition({
2726
+ transitionName: transition.name,
2727
+ startTime: transition.startTime,
2728
+ });
2729
+
2730
+ addTransitionCompleteCallbackToPendingTransition({
2731
+ transitionName: transition.name,
2732
+ startTime: transition.startTime,
2733
+ });
2734
+ });
2735
+
2736
+ clearTransitionsForLanes(finishedRoot, committedLanes);
2737
+ finishedWork.memoizedState.transitions = null;
2738
+ }
2739
+ }
2740
break;
2741
}
2742
case LegacyHiddenComponent:
packages/react-reconciler/src/ReactFiberCompleteWork.new.js
+26
@@ -153,6 +153,7 @@ import {
153
popRenderLanes,
154
getRenderTargetTime,
155
subtreeRenderLanes,
156
+ getWorkInProgressTransitions,
157
} from './ReactFiberWorkLoop.new';
158
import {
159
OffscreenLane,
@@ -862,6 +863,17 @@ function completeWork(
863
}
864
case HostRoot: {
865
const fiberRoot = (workInProgress.stateNode: FiberRoot);
866
+
867
+ if (enableTransitionTracing) {
868
+ const transitions = getWorkInProgressTransitions();
869
+ // We set the Passive flag here because if there are new transitions,
870
+ // we will need to schedule callbacks and process the transitions,
871
+ // which we do in the passive phase
872
+ if (transitions !== null) {
873
+ workInProgress.flags |= Passive;
874
+ }
875
+ }
876
+
877
if (enableCache) {
878
popRootTransition(fiberRoot, renderLanes);
879
@@ -918,6 +930,14 @@ function completeWork(
930
}
931
updateHostContainer(current, workInProgress);
932
bubbleProperties(workInProgress);
933
+ if (enableTransitionTracing) {
934
+ if ((workInProgress.subtreeFlags & Visibility) !== NoFlags) {
935
+ // If any of our suspense children toggle visibility, this means that
936
+ // the pending boundaries array needs to be updated, which we only
937
+ // do in the passive phase.
938
+ workInProgress.flags |= Passive;
939
+ }
940
+ }
941
return null;
942
}
943
case HostComponent: {
@@ -1187,6 +1207,12 @@ function completeWork(
1207
const offscreenFiber: Fiber = (workInProgress.child: any);
1208
offscreenFiber.flags |= Visibility;
1209
1210
+ // If the suspended state of the boundary changes, we need to schedule
1211
+ // a passive effect, which is when we process the transitions
1212
+ if (enableTransitionTracing) {
1213
+ offscreenFiber.flags |= Passive;
1214
+ }
1215
+
1216
// TODO: This will still suspend a synchronous tree if anything
1217
// in the concurrent tree already suspended during this render.
1218
// This is a known bug.
packages/react-reconciler/src/ReactFiberCompleteWork.old.js
+26
@@ -153,6 +153,7 @@ import {
153
popRenderLanes,
154
getRenderTargetTime,
155
subtreeRenderLanes,
156
+ getWorkInProgressTransitions,
157
} from './ReactFiberWorkLoop.old';
158
import {
159
OffscreenLane,
@@ -862,6 +863,17 @@ function completeWork(
863
}
864
case HostRoot: {
865
const fiberRoot = (workInProgress.stateNode: FiberRoot);
866
+
867
+ if (enableTransitionTracing) {
868
+ const transitions = getWorkInProgressTransitions();
869
+ // We set the Passive flag here because if there are new transitions,
870
+ // we will need to schedule callbacks and process the transitions,
871
+ // which we do in the passive phase
872
+ if (transitions !== null) {
873
+ workInProgress.flags |= Passive;
874
+ }
875
+ }
876
+
877
if (enableCache) {
878
popRootTransition(fiberRoot, renderLanes);
879
@@ -918,6 +930,14 @@ function completeWork(
930
}
931
updateHostContainer(current, workInProgress);
932
bubbleProperties(workInProgress);
933
+ if (enableTransitionTracing) {
934
+ if ((workInProgress.subtreeFlags & Visibility) !== NoFlags) {
935
+ // If any of our suspense children toggle visibility, this means that
936
+ // the pending boundaries array needs to be updated, which we only
937
+ // do in the passive phase.
938
+ workInProgress.flags |= Passive;
939
+ }
940
+ }
941
return null;
942
}
943
case HostComponent: {
@@ -1187,6 +1207,12 @@ function completeWork(
1207
const offscreenFiber: Fiber = (workInProgress.child: any);
1208
offscreenFiber.flags |= Visibility;
1209
1210
+ // If the suspended state of the boundary changes, we need to schedule
1211
+ // a passive effect, which is when we process the transitions
1212
+ if (enableTransitionTracing) {
1213
+ offscreenFiber.flags |= Passive;
1214
+ }
1215
+
1216
// TODO: This will still suspend a synchronous tree if anything
1217
// in the concurrent tree already suspended during this render.
1218
// This is a known bug.
packages/react-reconciler/src/ReactFiberWorkLoop.new.js
+29
-22
@@ -2322,27 +2322,6 @@ function commitRootImpl(
2322
// If layout work was scheduled, flush it now.
2323
flushSyncCallbacks();
2324
2325
- if (enableTransitionTracing) {
2326
- const prevPendingTransitionCallbacks = currentPendingTransitionCallbacks;
2327
- const prevRootTransitionCallbacks = root.transitionCallbacks;
2328
- if (
2329
- prevPendingTransitionCallbacks !== null &&
2330
- prevRootTransitionCallbacks !== null
2331
- ) {
2332
- // TODO(luna) Refactor this code into the Host Config
2333
- const endTime = now();
2334
- currentPendingTransitionCallbacks = null;
2335
-
2336
- scheduleCallback(IdleSchedulerPriority, () =>
2337
- processTransitionCallbacks(
2338
- prevPendingTransitionCallbacks,
2339
- endTime,
2340
- prevRootTransitionCallbacks,
2341
- ),
2342
- );
2343
- }
2344
- }
2345
-
2325
if (__DEV__) {
2326
if (enableDebugTracing) {
2327
logCommitStopped();
@@ -2457,7 +2436,7 @@ function flushPassiveEffectsImpl() {
2436
executionContext |= CommitContext;
2437
2438
commitPassiveUnmountEffects(root.current);
2460
- commitPassiveMountEffects(root, root.current);
2439
+ commitPassiveMountEffects(root, root.current, lanes);
2440
2441
// TODO: Move to commitPassiveMountEffects
2442
if (enableProfilerTimer && enableProfilerCommitHooks) {
@@ -2487,6 +2466,34 @@ function flushPassiveEffectsImpl() {
2466
2467
flushSyncCallbacks();
2468
2469
+ if (enableTransitionTracing) {
2470
+ const prevPendingTransitionCallbacks = currentPendingTransitionCallbacks;
2471
+ const prevRootTransitionCallbacks = root.transitionCallbacks;
2472
+ if (
2473
+ prevPendingTransitionCallbacks !== null &&
2474
+ prevRootTransitionCallbacks !== null
2475
+ ) {
2476
+ // TODO(luna) Refactor this code into the Host Config
2477
+ // TODO(luna) The end time here is not necessarily accurate
2478
+ // because passive effects could be called before paint
2479
+ // (synchronously) or after paint (normally). We need
2480
+ // to come up with a way to get the correct end time for both cases.
2481
+ // One solution is in the host config, if the passive effects
2482
+ // have not yet been run, make a call to flush the passive effects
2483
+ // right after paint.
2484
+ const endTime = now();
2485
+ currentPendingTransitionCallbacks = null;
2486
+
2487
+ scheduleCallback(IdleSchedulerPriority, () =>
2488
+ processTransitionCallbacks(
2489
+ prevPendingTransitionCallbacks,
2490
+ endTime,
2491
+ prevRootTransitionCallbacks,
2492
+ ),
2493
+ );
2494
+ }
2495
+ }
2496
+
2497
if (__DEV__) {
2498
// If additional passive effects were scheduled, increment a counter. If this
2499
// exceeds the limit, we'll fire a warning.
packages/react-reconciler/src/ReactFiberWorkLoop.old.js
+29
-22
@@ -2322,27 +2322,6 @@ function commitRootImpl(
2322
// If layout work was scheduled, flush it now.
2323
flushSyncCallbacks();
2324
2325
- if (enableTransitionTracing) {
2326
- const prevPendingTransitionCallbacks = currentPendingTransitionCallbacks;
2327
- const prevRootTransitionCallbacks = root.transitionCallbacks;
2328
- if (
2329
- prevPendingTransitionCallbacks !== null &&
2330
- prevRootTransitionCallbacks !== null
2331
- ) {
2332
- // TODO(luna) Refactor this code into the Host Config
2333
- const endTime = now();
2334
- currentPendingTransitionCallbacks = null;
2335
-
2336
- scheduleCallback(IdleSchedulerPriority, () =>
2337
- processTransitionCallbacks(
2338
- prevPendingTransitionCallbacks,
2339
- endTime,
2340
- prevRootTransitionCallbacks,
2341
- ),
2342
- );
2343
- }
2344
- }
2345
-
2325
if (__DEV__) {
2326
if (enableDebugTracing) {
2327
logCommitStopped();
@@ -2457,7 +2436,7 @@ function flushPassiveEffectsImpl() {
2436
executionContext |= CommitContext;
2437
2438
commitPassiveUnmountEffects(root.current);
2460
- commitPassiveMountEffects(root, root.current);
2439
+ commitPassiveMountEffects(root, root.current, lanes);
2440
2441
// TODO: Move to commitPassiveMountEffects
2442
if (enableProfilerTimer && enableProfilerCommitHooks) {
@@ -2487,6 +2466,34 @@ function flushPassiveEffectsImpl() {
2466
2467
flushSyncCallbacks();
2468
2469
+ if (enableTransitionTracing) {
2470
+ const prevPendingTransitionCallbacks = currentPendingTransitionCallbacks;
2471
+ const prevRootTransitionCallbacks = root.transitionCallbacks;
2472
+ if (
2473
+ prevPendingTransitionCallbacks !== null &&
2474
+ prevRootTransitionCallbacks !== null
2475
+ ) {
2476
+ // TODO(luna) Refactor this code into the Host Config
2477
+ // TODO(luna) The end time here is not necessarily accurate
2478
+ // because passive effects could be called before paint
2479
+ // (synchronously) or after paint (normally). We need
2480
+ // to come up with a way to get the correct end time for both cases.
2481
+ // One solution is in the host config, if the passive effects
2482
+ // have not yet been run, make a call to flush the passive effects
2483
+ // right after paint.
2484
+ const endTime = now();
2485
+ currentPendingTransitionCallbacks = null;
2486
+
2487
+ scheduleCallback(IdleSchedulerPriority, () =>
2488
+ processTransitionCallbacks(
2489
+ prevPendingTransitionCallbacks,
2490
+ endTime,
2491
+ prevRootTransitionCallbacks,
2492
+ ),
2493
+ );
2494
+ }
2495
+ }
2496
+
2497
if (__DEV__) {
2498
// If additional passive effects were scheduled, increment a counter. If this
2499
// exceeds the limit, we'll fire a warning.