From 68817d98263fa4e067df05bffab3a85c109001c6 Mon Sep 17 00:00:00 2001 From: Luna Ruan Date: Mon, 11 Apr 2022 16:56:20 -0500 Subject: [PATCH] [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 --- .../src/ReactFiberBeginWork.new.js | 18 +++++++----------- .../src/ReactFiberBeginWork.old.js | 18 +++++++----------- .../src/ReactFiberCommitWork.new.js | 5 ++--- .../src/ReactFiberCommitWork.old.js | 5 ++--- .../src/ReactFiberOffscreenComponent.js | 3 ++- .../react-reconciler/src/ReactFiberRoot.new.js | 10 ++++++++-- .../react-reconciler/src/ReactFiberRoot.old.js | 10 ++++++++-- .../src/ReactFiberSuspenseComponent.new.js | 1 + .../src/ReactFiberSuspenseComponent.old.js | 1 + .../ReactFiberTracingMarkerComponent.new.js | 10 ++++------ .../ReactFiberTracingMarkerComponent.old.js | 10 ++++------ 11 files changed, 46 insertions(+), 45 deletions(-) diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.new.js b/packages/react-reconciler/src/ReactFiberBeginWork.new.js index 430a46029ec57..849da96fb89ac 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.new.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.new.js @@ -236,7 +236,6 @@ import { markSkippedUpdateLanes, getWorkInProgressRoot, pushRenderLanes, - getWorkInProgressTransitions, } from './ReactFiberWorkLoop.new'; import {setWorkInProgressVersion} from './ReactMutableSource.new'; import {pushCacheProvider, CacheContext} from './ReactFiberCacheComponent.new'; @@ -652,9 +651,11 @@ function updateOffscreenComponent( // Rendering a hidden tree. if ((workInProgress.mode & ConcurrentMode) === NoMode) { // In legacy sync mode, don't defer the subtree. Render it now. + // TODO: Consider how Offscreen should work with transitions in the future const nextState: OffscreenState = { baseLanes: NoLanes, cachePool: null, + transitions: null, }; workInProgress.memoizedState = nextState; if (enableCache) { @@ -688,6 +689,7 @@ function updateOffscreenComponent( const nextState: OffscreenState = { baseLanes: nextBaseLanes, cachePool: spawnedCachePool, + transitions: null, }; workInProgress.memoizedState = nextState; workInProgress.updateQueue = null; @@ -723,6 +725,7 @@ function updateOffscreenComponent( const nextState: OffscreenState = { baseLanes: NoLanes, cachePool: null, + transitions: null, }; workInProgress.memoizedState = nextState; // Push the lanes that were skipped when we bailed out. @@ -1345,13 +1348,6 @@ function updateHostRoot(current, workInProgress, renderLanes) { } } - if (enableTransitionTracing) { - // FIXME: Slipped past code review. This is not a safe mutation: - // workInProgress.memoizedState is a shared object. Need to fix before - // rolling out the Transition Tracing experiment. - workInProgress.memoizedState.transitions = getWorkInProgressTransitions(); - } - // Caution: React DevTools currently depends on this property // being called "element". const nextChildren = nextState.element; @@ -1365,6 +1361,7 @@ function updateHostRoot(current, workInProgress, renderLanes) { element: nextChildren, isDehydrated: false, cache: nextState.cache, + pendingSuspenseBoundaries: nextState.pendingSuspenseBoundaries, transitions: nextState.transitions, }; const updateQueue: UpdateQueue = (workInProgress.updateQueue: any); @@ -1982,6 +1979,7 @@ function mountSuspenseOffscreenState(renderLanes: Lanes): OffscreenState { return { baseLanes: renderLanes, cachePool: getSuspendedCache(), + transitions: null, }; } @@ -2016,6 +2014,7 @@ function updateSuspenseOffscreenState( return { baseLanes: mergeLanes(prevOffscreenState.baseLanes, renderLanes), cachePool, + transitions: prevOffscreenState.transitions, }; } @@ -3582,9 +3581,6 @@ function attemptEarlyBailoutIfNoScheduledUpdate( const cache: Cache = current.memoizedState.cache; pushCacheProvider(workInProgress, cache); } - if (enableTransitionTracing) { - workInProgress.memoizedState.transitions = getWorkInProgressTransitions(); - } resetHydrationState(); break; case HostComponent: diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.old.js b/packages/react-reconciler/src/ReactFiberBeginWork.old.js index 05e769018c786..3459ba79b2fa1 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.old.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.old.js @@ -236,7 +236,6 @@ import { markSkippedUpdateLanes, getWorkInProgressRoot, pushRenderLanes, - getWorkInProgressTransitions, } from './ReactFiberWorkLoop.old'; import {setWorkInProgressVersion} from './ReactMutableSource.old'; import {pushCacheProvider, CacheContext} from './ReactFiberCacheComponent.old'; @@ -652,9 +651,11 @@ function updateOffscreenComponent( // Rendering a hidden tree. if ((workInProgress.mode & ConcurrentMode) === NoMode) { // In legacy sync mode, don't defer the subtree. Render it now. + // TODO: Consider how Offscreen should work with transitions in the future const nextState: OffscreenState = { baseLanes: NoLanes, cachePool: null, + transitions: null, }; workInProgress.memoizedState = nextState; if (enableCache) { @@ -688,6 +689,7 @@ function updateOffscreenComponent( const nextState: OffscreenState = { baseLanes: nextBaseLanes, cachePool: spawnedCachePool, + transitions: null, }; workInProgress.memoizedState = nextState; workInProgress.updateQueue = null; @@ -723,6 +725,7 @@ function updateOffscreenComponent( const nextState: OffscreenState = { baseLanes: NoLanes, cachePool: null, + transitions: null, }; workInProgress.memoizedState = nextState; // Push the lanes that were skipped when we bailed out. @@ -1345,13 +1348,6 @@ function updateHostRoot(current, workInProgress, renderLanes) { } } - if (enableTransitionTracing) { - // FIXME: Slipped past code review. This is not a safe mutation: - // workInProgress.memoizedState is a shared object. Need to fix before - // rolling out the Transition Tracing experiment. - workInProgress.memoizedState.transitions = getWorkInProgressTransitions(); - } - // Caution: React DevTools currently depends on this property // being called "element". const nextChildren = nextState.element; @@ -1365,6 +1361,7 @@ function updateHostRoot(current, workInProgress, renderLanes) { element: nextChildren, isDehydrated: false, cache: nextState.cache, + pendingSuspenseBoundaries: nextState.pendingSuspenseBoundaries, transitions: nextState.transitions, }; const updateQueue: UpdateQueue = (workInProgress.updateQueue: any); @@ -1982,6 +1979,7 @@ function mountSuspenseOffscreenState(renderLanes: Lanes): OffscreenState { return { baseLanes: renderLanes, cachePool: getSuspendedCache(), + transitions: null, }; } @@ -2016,6 +2014,7 @@ function updateSuspenseOffscreenState( return { baseLanes: mergeLanes(prevOffscreenState.baseLanes, renderLanes), cachePool, + transitions: prevOffscreenState.transitions, }; } @@ -3582,9 +3581,6 @@ function attemptEarlyBailoutIfNoScheduledUpdate( const cache: Cache = current.memoizedState.cache; pushCacheProvider(workInProgress, cache); } - if (enableTransitionTracing) { - workInProgress.memoizedState.transitions = getWorkInProgressTransitions(); - } resetHydrationState(); break; case HostComponent: diff --git a/packages/react-reconciler/src/ReactFiberCommitWork.new.js b/packages/react-reconciler/src/ReactFiberCommitWork.new.js index c9f40139f64e1..05f0b6d094969 100644 --- a/packages/react-reconciler/src/ReactFiberCommitWork.new.js +++ b/packages/react-reconciler/src/ReactFiberCommitWork.new.js @@ -2725,9 +2725,8 @@ function commitPassiveMountOnFiber( } if (enableTransitionTracing) { - const transitions = finishedWork.memoizedState.transitions; - if (transitions !== null) { - transitions.forEach(transition => { + if (committedTransitions !== null) { + committedTransitions.forEach(transition => { // TODO(luna) Do we want to log TransitionStart in the startTransition callback instead? addTransitionStartCallbackToPendingTransition({ transitionName: transition.name, diff --git a/packages/react-reconciler/src/ReactFiberCommitWork.old.js b/packages/react-reconciler/src/ReactFiberCommitWork.old.js index 731eaac9ef9ad..ee52b71c3c567 100644 --- a/packages/react-reconciler/src/ReactFiberCommitWork.old.js +++ b/packages/react-reconciler/src/ReactFiberCommitWork.old.js @@ -2725,9 +2725,8 @@ function commitPassiveMountOnFiber( } if (enableTransitionTracing) { - const transitions = finishedWork.memoizedState.transitions; - if (transitions !== null) { - transitions.forEach(transition => { + if (committedTransitions !== null) { + committedTransitions.forEach(transition => { // TODO(luna) Do we want to log TransitionStart in the startTransition callback instead? addTransitionStartCallbackToPendingTransition({ transitionName: transition.name, diff --git a/packages/react-reconciler/src/ReactFiberOffscreenComponent.js b/packages/react-reconciler/src/ReactFiberOffscreenComponent.js index 2d0fea0657a84..6a85b5de78cd9 100644 --- a/packages/react-reconciler/src/ReactFiberOffscreenComponent.js +++ b/packages/react-reconciler/src/ReactFiberOffscreenComponent.js @@ -10,7 +10,7 @@ import type {ReactNodeList, OffscreenMode} from 'shared/ReactTypes'; import type {Lanes} from './ReactFiberLane.old'; import type {SpawnedCachePool} from './ReactFiberCacheComponent.new'; - +import type {Transition} from './ReactFiberTracingMarkerComponent.new'; export type OffscreenProps = {| // TODO: Pick an API before exposing the Offscreen type. I've chosen an enum // for now, since we might have multiple variants. For example, hiding the @@ -30,6 +30,7 @@ export type OffscreenState = {| // order to unhide the component. baseLanes: Lanes, cachePool: SpawnedCachePool | null, + transitions: Set | null, |}; export type OffscreenInstance = {}; diff --git a/packages/react-reconciler/src/ReactFiberRoot.new.js b/packages/react-reconciler/src/ReactFiberRoot.new.js index ba8717a65e9e8..304073ed019df 100644 --- a/packages/react-reconciler/src/ReactFiberRoot.new.js +++ b/packages/react-reconciler/src/ReactFiberRoot.new.js @@ -15,7 +15,10 @@ import type { } from './ReactInternalTypes'; import type {RootTag} from './ReactRootTags'; import type {Cache} from './ReactFiberCacheComponent.new'; -import type {Transition} from './ReactFiberTracingMarkerComponent.new'; +import type { + PendingSuspenseBoundaries, + Transition, +} from './ReactFiberTracingMarkerComponent.new'; import {noTimeout, supportsHydration} from './ReactFiberHostConfig'; import {createHostRootFiber} from './ReactFiber.new'; @@ -42,7 +45,8 @@ export type RootState = { element: any, isDehydrated: boolean, cache: Cache, - transitions: Array | null, + pendingSuspenseBoundaries: PendingSuspenseBoundaries | null, + transitions: Set | null, }; function FiberRootNode( @@ -184,6 +188,7 @@ export function createFiberRoot( isDehydrated: hydrate, cache: initialCache, transitions: null, + pendingSuspenseBoundaries: null, }; uninitializedFiber.memoizedState = initialState; } else { @@ -192,6 +197,7 @@ export function createFiberRoot( isDehydrated: hydrate, cache: (null: any), // not enabled yet transitions: null, + pendingSuspenseBoundaries: null, }; uninitializedFiber.memoizedState = initialState; } diff --git a/packages/react-reconciler/src/ReactFiberRoot.old.js b/packages/react-reconciler/src/ReactFiberRoot.old.js index 1d5edd410cfef..3c4086394a1a6 100644 --- a/packages/react-reconciler/src/ReactFiberRoot.old.js +++ b/packages/react-reconciler/src/ReactFiberRoot.old.js @@ -15,7 +15,10 @@ import type { } from './ReactInternalTypes'; import type {RootTag} from './ReactRootTags'; import type {Cache} from './ReactFiberCacheComponent.old'; -import type {Transition} from './ReactFiberTracingMarkerComponent.old'; +import type { + PendingSuspenseBoundaries, + Transition, +} from './ReactFiberTracingMarkerComponent.old'; import {noTimeout, supportsHydration} from './ReactFiberHostConfig'; import {createHostRootFiber} from './ReactFiber.old'; @@ -42,7 +45,8 @@ export type RootState = { element: any, isDehydrated: boolean, cache: Cache, - transitions: Array | null, + pendingSuspenseBoundaries: PendingSuspenseBoundaries | null, + transitions: Set | null, }; function FiberRootNode( @@ -184,6 +188,7 @@ export function createFiberRoot( isDehydrated: hydrate, cache: initialCache, transitions: null, + pendingSuspenseBoundaries: null, }; uninitializedFiber.memoizedState = initialState; } else { @@ -192,6 +197,7 @@ export function createFiberRoot( isDehydrated: hydrate, cache: (null: any), // not enabled yet transitions: null, + pendingSuspenseBoundaries: null, }; uninitializedFiber.memoizedState = initialState; } diff --git a/packages/react-reconciler/src/ReactFiberSuspenseComponent.new.js b/packages/react-reconciler/src/ReactFiberSuspenseComponent.new.js index 54e37a9004d7f..a5952ace94745 100644 --- a/packages/react-reconciler/src/ReactFiberSuspenseComponent.new.js +++ b/packages/react-reconciler/src/ReactFiberSuspenseComponent.new.js @@ -29,6 +29,7 @@ export type SuspenseProps = {| suspenseCallback?: (Set | null) => mixed, unstable_expectedLoadTime?: number, + unstable_name?: string, |}; // A null SuspenseState represents an unsuspended normal Suspense boundary. diff --git a/packages/react-reconciler/src/ReactFiberSuspenseComponent.old.js b/packages/react-reconciler/src/ReactFiberSuspenseComponent.old.js index 95fb1b8c8ab42..2089dc6cac567 100644 --- a/packages/react-reconciler/src/ReactFiberSuspenseComponent.old.js +++ b/packages/react-reconciler/src/ReactFiberSuspenseComponent.old.js @@ -29,6 +29,7 @@ export type SuspenseProps = {| suspenseCallback?: (Set | null) => mixed, unstable_expectedLoadTime?: number, + unstable_name?: string, |}; // A null SuspenseState represents an unsuspended normal Suspense boundary. diff --git a/packages/react-reconciler/src/ReactFiberTracingMarkerComponent.new.js b/packages/react-reconciler/src/ReactFiberTracingMarkerComponent.new.js index cc3834f5cc6c3..29018efc96f9f 100644 --- a/packages/react-reconciler/src/ReactFiberTracingMarkerComponent.new.js +++ b/packages/react-reconciler/src/ReactFiberTracingMarkerComponent.new.js @@ -7,8 +7,9 @@ * @flow */ -import type {TransitionTracingCallbacks} from './ReactInternalTypes'; -import type {Fiber} from 'react-reconciler/src/ReactInternalTypes'; +import type {TransitionTracingCallbacks, Fiber} from './ReactInternalTypes'; +import type {OffscreenInstance} from './ReactFiberOffscreenComponent'; + import {enableTransitionTracing} from 'shared/ReactFeatureFlags'; export type SuspenseInfo = {name: string | null}; @@ -34,10 +35,7 @@ export type BatchConfigTransition = { _updatedFibers?: Set, }; -export type TransitionCallback = 0 | 1; - -export const TransitionStart = 0; -export const TransitionComplete = 1; +export type PendingSuspenseBoundaries = Map; export function processTransitionCallbacks( pendingTransitions: PendingTransitionCallbacks, diff --git a/packages/react-reconciler/src/ReactFiberTracingMarkerComponent.old.js b/packages/react-reconciler/src/ReactFiberTracingMarkerComponent.old.js index cc3834f5cc6c3..29018efc96f9f 100644 --- a/packages/react-reconciler/src/ReactFiberTracingMarkerComponent.old.js +++ b/packages/react-reconciler/src/ReactFiberTracingMarkerComponent.old.js @@ -7,8 +7,9 @@ * @flow */ -import type {TransitionTracingCallbacks} from './ReactInternalTypes'; -import type {Fiber} from 'react-reconciler/src/ReactInternalTypes'; +import type {TransitionTracingCallbacks, Fiber} from './ReactInternalTypes'; +import type {OffscreenInstance} from './ReactFiberOffscreenComponent'; + import {enableTransitionTracing} from 'shared/ReactFeatureFlags'; export type SuspenseInfo = {name: string | null}; @@ -34,10 +35,7 @@ export type BatchConfigTransition = { _updatedFibers?: Set, }; -export type TransitionCallback = 0 | 1; - -export const TransitionStart = 0; -export const TransitionComplete = 1; +export type PendingSuspenseBoundaries = Map; export function processTransitionCallbacks( pendingTransitions: PendingTransitionCallbacks,