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

[Transition Tracing] Add transition to OffscreenState and pendingSuspenseBoundaries to RootState (#24340)

In this PR we: Add transitions boilerplate to the OffscreenState. The transitions field will be null on initiation. During the commit phase, if there are any new transitions, we will add any new transitions (either as a result of a transition occurring or a parent suspense boundary completing) to the transitions field. Once the suspense boundary resolves, we no longer need to store the transitions on the boundary, so we can put this field on the Offscreen memoized state Add pendingSuspenseBoundaries boilerplate to the RootState. This field starts as null. During the commit phase, if a suspense boundary has either gone from fallback to resolved or from resolved to fallback, we will create a new Map if there isn't one, and if there is, we will add (if the boundary is a fallback) or remove the suspense boundary (if the boundary has resolved) from the map. Add an optional name field to the Suspense boundary

Luna Ruan committed Apr 11, 2022 at 16:56 UTC fea6f8da6ab669469f2fa3f18bd3a831f00ab284
11 files changed +46 -45
packages/react-reconciler/src/ReactFiberBeginWork.new.js
+7 -11
@@ -236,7 +236,6 @@ import {
236 markSkippedUpdateLanes,
237 getWorkInProgressRoot,
238 pushRenderLanes,
239 - getWorkInProgressTransitions,
239 } from './ReactFiberWorkLoop.new';
240 import {setWorkInProgressVersion} from './ReactMutableSource.new';
241 import {pushCacheProvider, CacheContext} from './ReactFiberCacheComponent.new';
@@ -652,9 +651,11 @@ function updateOffscreenComponent(
651 // Rendering a hidden tree.
652 if ((workInProgress.mode & ConcurrentMode) === NoMode) {
653 // In legacy sync mode, don't defer the subtree. Render it now.
654 + // TODO: Consider how Offscreen should work with transitions in the future
655 const nextState: OffscreenState = {
656 baseLanes: NoLanes,
657 cachePool: null,
658 + transitions: null,
659 };
660 workInProgress.memoizedState = nextState;
661 if (enableCache) {
@@ -688,6 +689,7 @@ function updateOffscreenComponent(
689 const nextState: OffscreenState = {
690 baseLanes: nextBaseLanes,
691 cachePool: spawnedCachePool,
692 + transitions: null,
693 };
694 workInProgress.memoizedState = nextState;
695 workInProgress.updateQueue = null;
@@ -723,6 +725,7 @@ function updateOffscreenComponent(
725 const nextState: OffscreenState = {
726 baseLanes: NoLanes,
727 cachePool: null,
728 + transitions: null,
729 };
730 workInProgress.memoizedState = nextState;
731 // Push the lanes that were skipped when we bailed out.
@@ -1345,13 +1348,6 @@ function updateHostRoot(current, workInProgress, renderLanes) {
1348 }
1349 }
1350
1348 - if (enableTransitionTracing) {
1349 - // FIXME: Slipped past code review. This is not a safe mutation:
1350 - // workInProgress.memoizedState is a shared object. Need to fix before
1351 - // rolling out the Transition Tracing experiment.
1352 - workInProgress.memoizedState.transitions = getWorkInProgressTransitions();
1353 - }
1354 -
1351 // Caution: React DevTools currently depends on this property
1352 // being called "element".
1353 const nextChildren = nextState.element;
@@ -1365,6 +1361,7 @@ function updateHostRoot(current, workInProgress, renderLanes) {
1361 element: nextChildren,
1362 isDehydrated: false,
1363 cache: nextState.cache,
1364 + pendingSuspenseBoundaries: nextState.pendingSuspenseBoundaries,
1365 transitions: nextState.transitions,
1366 };
1367 const updateQueue: UpdateQueue<RootState> = (workInProgress.updateQueue: any);
@@ -1982,6 +1979,7 @@ function mountSuspenseOffscreenState(renderLanes: Lanes): OffscreenState {
1979 return {
1980 baseLanes: renderLanes,
1981 cachePool: getSuspendedCache(),
1982 + transitions: null,
1983 };
1984 }
1985
@@ -2016,6 +2014,7 @@ function updateSuspenseOffscreenState(
2014 return {
2015 baseLanes: mergeLanes(prevOffscreenState.baseLanes, renderLanes),
2016 cachePool,
2017 + transitions: prevOffscreenState.transitions,
2018 };
2019 }
2020
@@ -3582,9 +3581,6 @@ function attemptEarlyBailoutIfNoScheduledUpdate(
3581 const cache: Cache = current.memoizedState.cache;
3582 pushCacheProvider(workInProgress, cache);
3583 }
3585 - if (enableTransitionTracing) {
3586 - workInProgress.memoizedState.transitions = getWorkInProgressTransitions();
3587 - }
3584 resetHydrationState();
3585 break;
3586 case HostComponent:
packages/react-reconciler/src/ReactFiberBeginWork.old.js
+7 -11
@@ -236,7 +236,6 @@ import {
236 markSkippedUpdateLanes,
237 getWorkInProgressRoot,
238 pushRenderLanes,
239 - getWorkInProgressTransitions,
239 } from './ReactFiberWorkLoop.old';
240 import {setWorkInProgressVersion} from './ReactMutableSource.old';
241 import {pushCacheProvider, CacheContext} from './ReactFiberCacheComponent.old';
@@ -652,9 +651,11 @@ function updateOffscreenComponent(
651 // Rendering a hidden tree.
652 if ((workInProgress.mode & ConcurrentMode) === NoMode) {
653 // In legacy sync mode, don't defer the subtree. Render it now.
654 + // TODO: Consider how Offscreen should work with transitions in the future
655 const nextState: OffscreenState = {
656 baseLanes: NoLanes,
657 cachePool: null,
658 + transitions: null,
659 };
660 workInProgress.memoizedState = nextState;
661 if (enableCache) {
@@ -688,6 +689,7 @@ function updateOffscreenComponent(
689 const nextState: OffscreenState = {
690 baseLanes: nextBaseLanes,
691 cachePool: spawnedCachePool,
692 + transitions: null,
693 };
694 workInProgress.memoizedState = nextState;
695 workInProgress.updateQueue = null;
@@ -723,6 +725,7 @@ function updateOffscreenComponent(
725 const nextState: OffscreenState = {
726 baseLanes: NoLanes,
727 cachePool: null,
728 + transitions: null,
729 };
730 workInProgress.memoizedState = nextState;
731 // Push the lanes that were skipped when we bailed out.
@@ -1345,13 +1348,6 @@ function updateHostRoot(current, workInProgress, renderLanes) {
1348 }
1349 }
1350
1348 - if (enableTransitionTracing) {
1349 - // FIXME: Slipped past code review. This is not a safe mutation:
1350 - // workInProgress.memoizedState is a shared object. Need to fix before
1351 - // rolling out the Transition Tracing experiment.
1352 - workInProgress.memoizedState.transitions = getWorkInProgressTransitions();
1353 - }
1354 -
1351 // Caution: React DevTools currently depends on this property
1352 // being called "element".
1353 const nextChildren = nextState.element;
@@ -1365,6 +1361,7 @@ function updateHostRoot(current, workInProgress, renderLanes) {
1361 element: nextChildren,
1362 isDehydrated: false,
1363 cache: nextState.cache,
1364 + pendingSuspenseBoundaries: nextState.pendingSuspenseBoundaries,
1365 transitions: nextState.transitions,
1366 };
1367 const updateQueue: UpdateQueue<RootState> = (workInProgress.updateQueue: any);
@@ -1982,6 +1979,7 @@ function mountSuspenseOffscreenState(renderLanes: Lanes): OffscreenState {
1979 return {
1980 baseLanes: renderLanes,
1981 cachePool: getSuspendedCache(),
1982 + transitions: null,
1983 };
1984 }
1985
@@ -2016,6 +2014,7 @@ function updateSuspenseOffscreenState(
2014 return {
2015 baseLanes: mergeLanes(prevOffscreenState.baseLanes, renderLanes),
2016 cachePool,
2017 + transitions: prevOffscreenState.transitions,
2018 };
2019 }
2020
@@ -3582,9 +3581,6 @@ function attemptEarlyBailoutIfNoScheduledUpdate(
3581 const cache: Cache = current.memoizedState.cache;
3582 pushCacheProvider(workInProgress, cache);
3583 }
3585 - if (enableTransitionTracing) {
3586 - workInProgress.memoizedState.transitions = getWorkInProgressTransitions();
3587 - }
3584 resetHydrationState();
3585 break;
3586 case HostComponent:
packages/react-reconciler/src/ReactFiberCommitWork.new.js
+2 -3
@@ -2725,9 +2725,8 @@ function commitPassiveMountOnFiber(
2725 }
2726
2727 if (enableTransitionTracing) {
2728 - const transitions = finishedWork.memoizedState.transitions;
2729 - if (transitions !== null) {
2730 - transitions.forEach(transition => {
2728 + if (committedTransitions !== null) {
2729 + committedTransitions.forEach(transition => {
2730 // TODO(luna) Do we want to log TransitionStart in the startTransition callback instead?
2731 addTransitionStartCallbackToPendingTransition({
2732 transitionName: transition.name,
packages/react-reconciler/src/ReactFiberCommitWork.old.js
+2 -3
@@ -2725,9 +2725,8 @@ function commitPassiveMountOnFiber(
2725 }
2726
2727 if (enableTransitionTracing) {
2728 - const transitions = finishedWork.memoizedState.transitions;
2729 - if (transitions !== null) {
2730 - transitions.forEach(transition => {
2728 + if (committedTransitions !== null) {
2729 + committedTransitions.forEach(transition => {
2730 // TODO(luna) Do we want to log TransitionStart in the startTransition callback instead?
2731 addTransitionStartCallbackToPendingTransition({
2732 transitionName: transition.name,
packages/react-reconciler/src/ReactFiberOffscreenComponent.js
+2 -1
@@ -10,7 +10,7 @@
10 import type {ReactNodeList, OffscreenMode} from 'shared/ReactTypes';
11 import type {Lanes} from './ReactFiberLane.old';
12 import type {SpawnedCachePool} from './ReactFiberCacheComponent.new';
13 -
13 +import type {Transition} from './ReactFiberTracingMarkerComponent.new';
14 export type OffscreenProps = {|
15 // TODO: Pick an API before exposing the Offscreen type. I've chosen an enum
16 // for now, since we might have multiple variants. For example, hiding the
@@ -30,6 +30,7 @@ export type OffscreenState = {|
30 // order to unhide the component.
31 baseLanes: Lanes,
32 cachePool: SpawnedCachePool | null,
33 + transitions: Set<Transition> | null,
34 |};
35
36 export type OffscreenInstance = {};
packages/react-reconciler/src/ReactFiberRoot.new.js
+8 -2
@@ -15,7 +15,10 @@ import type {
15 } from './ReactInternalTypes';
16 import type {RootTag} from './ReactRootTags';
17 import type {Cache} from './ReactFiberCacheComponent.new';
18 -import type {Transition} from './ReactFiberTracingMarkerComponent.new';
18 +import type {
19 + PendingSuspenseBoundaries,
20 + Transition,
21 +} from './ReactFiberTracingMarkerComponent.new';
22
23 import {noTimeout, supportsHydration} from './ReactFiberHostConfig';
24 import {createHostRootFiber} from './ReactFiber.new';
@@ -42,7 +45,8 @@ export type RootState = {
45 element: any,
46 isDehydrated: boolean,
47 cache: Cache,
45 - transitions: Array<Transition> | null,
48 + pendingSuspenseBoundaries: PendingSuspenseBoundaries | null,
49 + transitions: Set<Transition> | null,
50 };
51
52 function FiberRootNode(
@@ -184,6 +188,7 @@ export function createFiberRoot(
188 isDehydrated: hydrate,
189 cache: initialCache,
190 transitions: null,
191 + pendingSuspenseBoundaries: null,
192 };
193 uninitializedFiber.memoizedState = initialState;
194 } else {
@@ -192,6 +197,7 @@ export function createFiberRoot(
197 isDehydrated: hydrate,
198 cache: (null: any), // not enabled yet
199 transitions: null,
200 + pendingSuspenseBoundaries: null,
201 };
202 uninitializedFiber.memoizedState = initialState;
203 }
packages/react-reconciler/src/ReactFiberRoot.old.js
+8 -2
@@ -15,7 +15,10 @@ import type {
15 } from './ReactInternalTypes';
16 import type {RootTag} from './ReactRootTags';
17 import type {Cache} from './ReactFiberCacheComponent.old';
18 -import type {Transition} from './ReactFiberTracingMarkerComponent.old';
18 +import type {
19 + PendingSuspenseBoundaries,
20 + Transition,
21 +} from './ReactFiberTracingMarkerComponent.old';
22
23 import {noTimeout, supportsHydration} from './ReactFiberHostConfig';
24 import {createHostRootFiber} from './ReactFiber.old';
@@ -42,7 +45,8 @@ export type RootState = {
45 element: any,
46 isDehydrated: boolean,
47 cache: Cache,
45 - transitions: Array<Transition> | null,
48 + pendingSuspenseBoundaries: PendingSuspenseBoundaries | null,
49 + transitions: Set<Transition> | null,
50 };
51
52 function FiberRootNode(
@@ -184,6 +188,7 @@ export function createFiberRoot(
188 isDehydrated: hydrate,
189 cache: initialCache,
190 transitions: null,
191 + pendingSuspenseBoundaries: null,
192 };
193 uninitializedFiber.memoizedState = initialState;
194 } else {
@@ -192,6 +197,7 @@ export function createFiberRoot(
197 isDehydrated: hydrate,
198 cache: (null: any), // not enabled yet
199 transitions: null,
200 + pendingSuspenseBoundaries: null,
201 };
202 uninitializedFiber.memoizedState = initialState;
203 }
packages/react-reconciler/src/ReactFiberSuspenseComponent.new.js
+1
@@ -29,6 +29,7 @@ export type SuspenseProps = {|
29 suspenseCallback?: (Set<Wakeable> | null) => mixed,
30
31 unstable_expectedLoadTime?: number,
32 + unstable_name?: string,
33 |};
34
35 // A null SuspenseState represents an unsuspended normal Suspense boundary.
packages/react-reconciler/src/ReactFiberSuspenseComponent.old.js
+1
@@ -29,6 +29,7 @@ export type SuspenseProps = {|
29 suspenseCallback?: (Set<Wakeable> | null) => mixed,
30
31 unstable_expectedLoadTime?: number,
32 + unstable_name?: string,
33 |};
34
35 // A null SuspenseState represents an unsuspended normal Suspense boundary.
packages/react-reconciler/src/ReactFiberTracingMarkerComponent.new.js
+4 -6
@@ -7,8 +7,9 @@
7 * @flow
8 */
9
10 -import type {TransitionTracingCallbacks} from './ReactInternalTypes';
11 -import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
10 +import type {TransitionTracingCallbacks, Fiber} from './ReactInternalTypes';
11 +import type {OffscreenInstance} from './ReactFiberOffscreenComponent';
12 +
13 import {enableTransitionTracing} from 'shared/ReactFeatureFlags';
14
15 export type SuspenseInfo = {name: string | null};
@@ -34,10 +35,7 @@ export type BatchConfigTransition = {
35 _updatedFibers?: Set<Fiber>,
36 };
37
37 -export type TransitionCallback = 0 | 1;
38 -
39 -export const TransitionStart = 0;
40 -export const TransitionComplete = 1;
38 +export type PendingSuspenseBoundaries = Map<OffscreenInstance, SuspenseInfo>;
39
40 export function processTransitionCallbacks(
41 pendingTransitions: PendingTransitionCallbacks,
packages/react-reconciler/src/ReactFiberTracingMarkerComponent.old.js
+4 -6
@@ -7,8 +7,9 @@
7 * @flow
8 */
9
10 -import type {TransitionTracingCallbacks} from './ReactInternalTypes';
11 -import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
10 +import type {TransitionTracingCallbacks, Fiber} from './ReactInternalTypes';
11 +import type {OffscreenInstance} from './ReactFiberOffscreenComponent';
12 +
13 import {enableTransitionTracing} from 'shared/ReactFeatureFlags';
14
15 export type SuspenseInfo = {name: string | null};
@@ -34,10 +35,7 @@ export type BatchConfigTransition = {
35 _updatedFibers?: Set<Fiber>,
36 };
37
37 -export type TransitionCallback = 0 | 1;
38 -
39 -export const TransitionStart = 0;
40 -export const TransitionComplete = 1;
38 +export type PendingSuspenseBoundaries = Map<OffscreenInstance, SuspenseInfo>;
39
40 export function processTransitionCallbacks(
41 pendingTransitions: PendingTransitionCallbacks,