diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.new.js b/packages/react-reconciler/src/ReactFiberBeginWork.new.js index e4f4d7173b4b2..2816e72c60078 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.new.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.new.js @@ -27,6 +27,7 @@ import type { OffscreenProps, OffscreenState, OffscreenQueue, + OffscreenInstance, } from './ReactFiberOffscreenComponent'; import type { Cache, @@ -262,8 +263,9 @@ import { getPendingTransitions, } from './ReactFiberTransition.new'; import { - getTracingMarkers, - pushTracingMarker, + getMarkerInstances, + pushMarkerInstance, + pushRootMarkerInstance, } from './ReactFiberTracingMarkerComponent.new'; const ReactCurrentOwner = ReactSharedInternals.ReactCurrentOwner; @@ -779,7 +781,10 @@ function updateOffscreenComponent( if (enableTransitionTracing) { // We have now gone from hidden to visible, so any transitions should // be added to the stack to get added to any Offscreen/suspense children - transitions = workInProgress.stateNode.transitions; + const instance: OffscreenInstance | null = workInProgress.stateNode; + if (instance !== null && instance.transitions != null) { + transitions = Array.from(instance.transitions); + } } pushTransition(workInProgress, prevCachePool, transitions); @@ -909,7 +914,10 @@ function updateTracingMarkerComponent( } } - pushTracingMarker(workInProgress); + const instance: TracingMarkerInstance | null = workInProgress.stateNode; + if (instance !== null) { + pushMarkerInstance(workInProgress, instance); + } const nextChildren = workInProgress.pendingProps.children; reconcileChildren(current, workInProgress, nextChildren, renderLanes); return workInProgress.child; @@ -1313,6 +1321,10 @@ function updateHostRoot(current, workInProgress, renderLanes) { const root: FiberRoot = workInProgress.stateNode; pushRootTransition(workInProgress, root, renderLanes); + if (enableTransitionTracing) { + pushRootMarkerInstance(workInProgress); + } + if (enableCache) { const nextCache: Cache = nextState.cache; pushCacheProvider(workInProgress, nextCache); @@ -2098,10 +2110,10 @@ function updateSuspenseComponent(current, workInProgress, renderLanes) { const currentTransitions = getPendingTransitions(); if (currentTransitions !== null) { // If there are no transitions, we don't need to keep track of tracing markers - const currentTracingMarkers = getTracingMarkers(); + const parentMarkerInstances = getMarkerInstances(); const primaryChildUpdateQueue: OffscreenQueue = { transitions: currentTransitions, - tracingMarkers: currentTracingMarkers, + markerInstances: parentMarkerInstances, }; primaryChildFragment.updateQueue = primaryChildUpdateQueue; } @@ -2188,10 +2200,10 @@ function updateSuspenseComponent(current, workInProgress, renderLanes) { if (enableTransitionTracing) { const currentTransitions = getPendingTransitions(); if (currentTransitions !== null) { - const currentTracingMarkers = getTracingMarkers(); + const parentMarkerInstances = getMarkerInstances(); const primaryChildUpdateQueue: OffscreenQueue = { transitions: currentTransitions, - tracingMarkers: currentTracingMarkers, + markerInstances: parentMarkerInstances, }; primaryChildFragment.updateQueue = primaryChildUpdateQueue; } @@ -3509,6 +3521,10 @@ function attemptEarlyBailoutIfNoScheduledUpdate( const root: FiberRoot = workInProgress.stateNode; pushRootTransition(workInProgress, root, renderLanes); + if (enableTransitionTracing) { + pushRootMarkerInstance(workInProgress); + } + if (enableCache) { const cache: Cache = current.memoizedState.cache; pushCacheProvider(workInProgress, cache); @@ -3694,7 +3710,10 @@ function attemptEarlyBailoutIfNoScheduledUpdate( } case TracingMarkerComponent: { if (enableTransitionTracing) { - pushTracingMarker(workInProgress); + const instance: TracingMarkerInstance | null = workInProgress.stateNode; + if (instance !== null) { + pushMarkerInstance(workInProgress, instance); + } } } } diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.old.js b/packages/react-reconciler/src/ReactFiberBeginWork.old.js index ca25b272e473f..8b19780af34df 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.old.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.old.js @@ -27,6 +27,7 @@ import type { OffscreenProps, OffscreenState, OffscreenQueue, + OffscreenInstance, } from './ReactFiberOffscreenComponent'; import type { Cache, @@ -259,8 +260,9 @@ import { getPendingTransitions, } from './ReactFiberTransition.old'; import { - getTracingMarkers, - pushTracingMarker, + getMarkerInstances, + pushMarkerInstance, + pushRootMarkerInstance, } from './ReactFiberTracingMarkerComponent.old'; const ReactCurrentOwner = ReactSharedInternals.ReactCurrentOwner; @@ -784,7 +786,10 @@ function updateOffscreenComponent( if (enableTransitionTracing) { // We have now gone from hidden to visible, so any transitions should // be added to the stack to get added to any Offscreen/suspense children - transitions = workInProgress.stateNode.transitions; + const instance: OffscreenInstance | null = workInProgress.stateNode; + if (instance !== null && instance.transitions != null) { + transitions = Array.from(instance.transitions); + } } pushTransition(workInProgress, prevCachePool, transitions); @@ -909,7 +914,10 @@ function updateTracingMarkerComponent( } } - pushTracingMarker(workInProgress); + const instance: TracingMarkerInstance | null = workInProgress.stateNode; + if (instance !== null) { + pushMarkerInstance(workInProgress, instance); + } const nextChildren = workInProgress.pendingProps.children; reconcileChildren(current, workInProgress, nextChildren, renderLanes); return workInProgress.child; @@ -1313,6 +1321,10 @@ function updateHostRoot(current, workInProgress, renderLanes) { const root: FiberRoot = workInProgress.stateNode; pushRootTransition(workInProgress, root, renderLanes); + if (enableTransitionTracing) { + pushRootMarkerInstance(workInProgress); + } + if (enableCache) { const nextCache: Cache = nextState.cache; pushCacheProvider(workInProgress, nextCache); @@ -2114,10 +2126,10 @@ function updateSuspenseComponent(current, workInProgress, renderLanes) { const currentTransitions = getPendingTransitions(); if (currentTransitions !== null) { // If there are no transitions, we don't need to keep track of tracing markers - const currentTracingMarkers = getTracingMarkers(); + const parentMarkerInstances = getMarkerInstances(); const primaryChildUpdateQueue: OffscreenQueue = { transitions: currentTransitions, - tracingMarkers: currentTracingMarkers, + markerInstances: parentMarkerInstances, }; primaryChildFragment.updateQueue = primaryChildUpdateQueue; } @@ -2200,10 +2212,10 @@ function updateSuspenseComponent(current, workInProgress, renderLanes) { if (enableTransitionTracing) { const currentTransitions = getPendingTransitions(); if (currentTransitions !== null) { - const currentTracingMarkers = getTracingMarkers(); + const parentMarkerInstances = getMarkerInstances(); const primaryChildUpdateQueue: OffscreenQueue = { transitions: currentTransitions, - tracingMarkers: currentTracingMarkers, + markerInstances: parentMarkerInstances, }; primaryChildFragment.updateQueue = primaryChildUpdateQueue; } @@ -3510,6 +3522,10 @@ function attemptEarlyBailoutIfNoScheduledUpdate( const root: FiberRoot = workInProgress.stateNode; pushRootTransition(workInProgress, root, renderLanes); + if (enableTransitionTracing) { + pushRootMarkerInstance(workInProgress); + } + if (enableCache) { const cache: Cache = current.memoizedState.cache; pushCacheProvider(workInProgress, cache); @@ -3702,7 +3718,10 @@ function attemptEarlyBailoutIfNoScheduledUpdate( } case TracingMarkerComponent: { if (enableTransitionTracing) { - pushTracingMarker(workInProgress); + const instance: TracingMarkerInstance | null = workInProgress.stateNode; + if (instance !== null) { + pushMarkerInstance(workInProgress, instance); + } } } } diff --git a/packages/react-reconciler/src/ReactFiberCommitWork.new.js b/packages/react-reconciler/src/ReactFiberCommitWork.new.js index da00d7761f9c5..30709cffa5061 100644 --- a/packages/react-reconciler/src/ReactFiberCommitWork.new.js +++ b/packages/react-reconciler/src/ReactFiberCommitWork.new.js @@ -2811,47 +2811,35 @@ function commitPassiveMountOnFiber( // Get the transitions that were initiatized during the render // and add a start transition callback for each of them const root = finishedWork.stateNode; - let incompleteTransitions = root.incompleteTransitions; + const incompleteTransitions = root.incompleteTransitions; // Initial render if (committedTransitions !== null) { - if (incompleteTransitions === null) { - root.incompleteTransitions = incompleteTransitions = new Map(); - } - committedTransitions.forEach(transition => { addTransitionStartCallbackToPendingTransition({ transitionName: transition.name, startTime: transition.startTime, }); - - if (!incompleteTransitions.has(transition)) { - incompleteTransitions.set(transition, null); - } }); clearTransitionsForLanes(finishedRoot, committedLanes); } - if (incompleteTransitions !== null) { - incompleteTransitions.forEach((pendingBoundaries, transition) => { - if (pendingBoundaries === null || pendingBoundaries.size === 0) { + incompleteTransitions.forEach( + ({pendingSuspenseBoundaries}, transition) => { + if ( + pendingSuspenseBoundaries === null || + pendingSuspenseBoundaries.size === 0 + ) { addTransitionCompleteCallbackToPendingTransition({ transitionName: transition.name, startTime: transition.startTime, }); incompleteTransitions.delete(transition); } - }); - } + }, + ); - // If there are no more pending suspense boundaries we - // clear the transitions because they are all complete. - if ( - incompleteTransitions === null || - incompleteTransitions.size === 0 - ) { - root.incompleteTransitions = null; - } + clearTransitionsForLanes(finishedRoot, committedLanes); } break; } @@ -2896,14 +2884,6 @@ function commitPassiveMountOnFiber( if (isFallback) { const transitions = queue.transitions; let prevTransitions = instance.transitions; - let rootIncompleteTransitions = finishedRoot.incompleteTransitions; - - // We lazily instantiate transition tracing relevant maps - // and sets in the commit phase as we need to use them. We only - // instantiate them in the fallback phase on an as needed basis - if (rootIncompleteTransitions === null) { - finishedRoot.incompleteTransitions = rootIncompleteTransitions = new Map(); - } if (instance.pendingMarkers === null) { instance.pendingMarkers = new Set(); } @@ -2911,56 +2891,43 @@ function commitPassiveMountOnFiber( instance.transitions = prevTransitions = new Set(); } - // TODO(luna): Combine the root code with the tracing marker code if (transitions !== null) { transitions.forEach(transition => { // Add all the transitions saved in the update queue during // the render phase (ie the transitions associated with this boundary) // into the transitions set. prevTransitions.add(transition); - - // Add the root transition's pending suspense boundary set to - // the queue's marker set. We will iterate through the marker - // set when we toggle state on the suspense boundary and - // add or remove the pending suspense boundaries as needed. - if (rootIncompleteTransitions !== null) { - if (!rootIncompleteTransitions.has(transition)) { - rootIncompleteTransitions.set(transition, new Map()); - } - instance.pendingMarkers.add( - rootIncompleteTransitions.get(transition), - ); - } }); } - const tracingMarkers = queue.tracingMarkers; - if (tracingMarkers !== null) { - tracingMarkers.forEach(marker => { - const markerInstance = marker.stateNode; + const markerInstances = queue.markerInstances; + if (markerInstances !== null) { + markerInstances.forEach(markerInstance => { + if (markerInstance.pendingSuspenseBoundaries === null) { + markerInstance.pendingSuspenseBoundaries = new Map(); + } + + const markerTransitions = markerInstance.transitions; // There should only be a few tracing marker transitions because // they should be only associated with the transition that // caused them - markerInstance.transitions.forEach(transition => { - if (instance.transitions.has(transition)) { - instance.pendingMarkers.add( - markerInstance.pendingSuspenseBoundaries, - ); - } - }); + if (markerTransitions !== null) { + markerTransitions.forEach(transition => { + if (instance.transitions.has(transition)) { + instance.pendingMarkers.add( + markerInstance.pendingSuspenseBoundaries, + ); + } + }); + } }); } } - commitTransitionProgress(finishedWork); - - if ( - instance.pendingMarkers === null || - instance.pendingMarkers.size === 0 - ) { - finishedWork.updateQueue = null; - } + finishedWork.updateQueue = null; } + + commitTransitionProgress(finishedWork); } break; diff --git a/packages/react-reconciler/src/ReactFiberCommitWork.old.js b/packages/react-reconciler/src/ReactFiberCommitWork.old.js index 677db2f10b0ea..7f596a12641cc 100644 --- a/packages/react-reconciler/src/ReactFiberCommitWork.old.js +++ b/packages/react-reconciler/src/ReactFiberCommitWork.old.js @@ -2811,47 +2811,35 @@ function commitPassiveMountOnFiber( // Get the transitions that were initiatized during the render // and add a start transition callback for each of them const root = finishedWork.stateNode; - let incompleteTransitions = root.incompleteTransitions; + const incompleteTransitions = root.incompleteTransitions; // Initial render if (committedTransitions !== null) { - if (incompleteTransitions === null) { - root.incompleteTransitions = incompleteTransitions = new Map(); - } - committedTransitions.forEach(transition => { addTransitionStartCallbackToPendingTransition({ transitionName: transition.name, startTime: transition.startTime, }); - - if (!incompleteTransitions.has(transition)) { - incompleteTransitions.set(transition, null); - } }); clearTransitionsForLanes(finishedRoot, committedLanes); } - if (incompleteTransitions !== null) { - incompleteTransitions.forEach((pendingBoundaries, transition) => { - if (pendingBoundaries === null || pendingBoundaries.size === 0) { + incompleteTransitions.forEach( + ({pendingSuspenseBoundaries}, transition) => { + if ( + pendingSuspenseBoundaries === null || + pendingSuspenseBoundaries.size === 0 + ) { addTransitionCompleteCallbackToPendingTransition({ transitionName: transition.name, startTime: transition.startTime, }); incompleteTransitions.delete(transition); } - }); - } + }, + ); - // If there are no more pending suspense boundaries we - // clear the transitions because they are all complete. - if ( - incompleteTransitions === null || - incompleteTransitions.size === 0 - ) { - root.incompleteTransitions = null; - } + clearTransitionsForLanes(finishedRoot, committedLanes); } break; } @@ -2896,14 +2884,6 @@ function commitPassiveMountOnFiber( if (isFallback) { const transitions = queue.transitions; let prevTransitions = instance.transitions; - let rootIncompleteTransitions = finishedRoot.incompleteTransitions; - - // We lazily instantiate transition tracing relevant maps - // and sets in the commit phase as we need to use them. We only - // instantiate them in the fallback phase on an as needed basis - if (rootIncompleteTransitions === null) { - finishedRoot.incompleteTransitions = rootIncompleteTransitions = new Map(); - } if (instance.pendingMarkers === null) { instance.pendingMarkers = new Set(); } @@ -2911,56 +2891,43 @@ function commitPassiveMountOnFiber( instance.transitions = prevTransitions = new Set(); } - // TODO(luna): Combine the root code with the tracing marker code if (transitions !== null) { transitions.forEach(transition => { // Add all the transitions saved in the update queue during // the render phase (ie the transitions associated with this boundary) // into the transitions set. prevTransitions.add(transition); - - // Add the root transition's pending suspense boundary set to - // the queue's marker set. We will iterate through the marker - // set when we toggle state on the suspense boundary and - // add or remove the pending suspense boundaries as needed. - if (rootIncompleteTransitions !== null) { - if (!rootIncompleteTransitions.has(transition)) { - rootIncompleteTransitions.set(transition, new Map()); - } - instance.pendingMarkers.add( - rootIncompleteTransitions.get(transition), - ); - } }); } - const tracingMarkers = queue.tracingMarkers; - if (tracingMarkers !== null) { - tracingMarkers.forEach(marker => { - const markerInstance = marker.stateNode; + const markerInstances = queue.markerInstances; + if (markerInstances !== null) { + markerInstances.forEach(markerInstance => { + if (markerInstance.pendingSuspenseBoundaries === null) { + markerInstance.pendingSuspenseBoundaries = new Map(); + } + + const markerTransitions = markerInstance.transitions; // There should only be a few tracing marker transitions because // they should be only associated with the transition that // caused them - markerInstance.transitions.forEach(transition => { - if (instance.transitions.has(transition)) { - instance.pendingMarkers.add( - markerInstance.pendingSuspenseBoundaries, - ); - } - }); + if (markerTransitions !== null) { + markerTransitions.forEach(transition => { + if (instance.transitions.has(transition)) { + instance.pendingMarkers.add( + markerInstance.pendingSuspenseBoundaries, + ); + } + }); + } }); } } - commitTransitionProgress(finishedWork); - - if ( - instance.pendingMarkers === null || - instance.pendingMarkers.size === 0 - ) { - finishedWork.updateQueue = null; - } + finishedWork.updateQueue = null; } + + commitTransitionProgress(finishedWork); } break; diff --git a/packages/react-reconciler/src/ReactFiberCompleteWork.new.js b/packages/react-reconciler/src/ReactFiberCompleteWork.new.js index 913abb20c4130..3fb9f8dfb1263 100644 --- a/packages/react-reconciler/src/ReactFiberCompleteWork.new.js +++ b/packages/react-reconciler/src/ReactFiberCompleteWork.new.js @@ -28,6 +28,7 @@ import type { SuspenseListRenderState, } from './ReactFiberSuspenseComponent.new'; import type {OffscreenState} from './ReactFiberOffscreenComponent'; +import type {TracingMarkerInstance} from './ReactFiberTracingMarkerComponent.new'; import type {Cache} from './ReactFiberCacheComponent.new'; import { enableSuspenseAvoidThisFallback, @@ -164,7 +165,10 @@ import {transferActualDuration} from './ReactProfilerTimer.new'; import {popCacheProvider} from './ReactFiberCacheComponent.new'; import {popTreeContext} from './ReactFiberTreeContext.new'; import {popRootTransition, popTransition} from './ReactFiberTransition.new'; -import {popTracingMarker} from './ReactFiberTracingMarkerComponent.new'; +import { + popMarkerInstance, + popRootMarkerInstance, +} from './ReactFiberTracingMarkerComponent.new'; function markUpdate(workInProgress: Fiber) { // Tag the fiber with an update effect. This turns a Placement into @@ -900,6 +904,11 @@ function completeWork( } popCacheProvider(workInProgress, cache); } + + if (enableTransitionTracing) { + popRootMarkerInstance(workInProgress); + } + popRootTransition(workInProgress, fiberRoot, renderLanes); popHostContainer(workInProgress); popTopLevelLegacyContextObject(workInProgress); @@ -1579,7 +1588,10 @@ function completeWork( } case TracingMarkerComponent: { if (enableTransitionTracing) { - popTracingMarker(workInProgress); + const instance: TracingMarkerInstance | null = workInProgress.stateNode; + if (instance !== null) { + popMarkerInstance(workInProgress); + } bubbleProperties(workInProgress); if ( diff --git a/packages/react-reconciler/src/ReactFiberCompleteWork.old.js b/packages/react-reconciler/src/ReactFiberCompleteWork.old.js index 1fc8a388ea2d7..6dd8fb07ab09a 100644 --- a/packages/react-reconciler/src/ReactFiberCompleteWork.old.js +++ b/packages/react-reconciler/src/ReactFiberCompleteWork.old.js @@ -29,6 +29,7 @@ import type { } from './ReactFiberSuspenseComponent.old'; import type {SuspenseContext} from './ReactFiberSuspenseContext.old'; import type {OffscreenState} from './ReactFiberOffscreenComponent'; +import type {TracingMarkerInstance} from './ReactFiberTracingMarkerComponent.old'; import type {Cache} from './ReactFiberCacheComponent.old'; import { enableSuspenseAvoidThisFallback, @@ -164,7 +165,10 @@ import {transferActualDuration} from './ReactProfilerTimer.old'; import {popCacheProvider} from './ReactFiberCacheComponent.old'; import {popTreeContext} from './ReactFiberTreeContext.old'; import {popRootTransition, popTransition} from './ReactFiberTransition.old'; -import {popTracingMarker} from './ReactFiberTracingMarkerComponent.old'; +import { + popMarkerInstance, + popRootMarkerInstance, +} from './ReactFiberTracingMarkerComponent.old'; function markUpdate(workInProgress: Fiber) { // Tag the fiber with an update effect. This turns a Placement into @@ -900,6 +904,11 @@ function completeWork( } popCacheProvider(workInProgress, cache); } + + if (enableTransitionTracing) { + popRootMarkerInstance(workInProgress); + } + popRootTransition(workInProgress, fiberRoot, renderLanes); popHostContainer(workInProgress); popTopLevelLegacyContextObject(workInProgress); @@ -1579,7 +1588,10 @@ function completeWork( } case TracingMarkerComponent: { if (enableTransitionTracing) { - popTracingMarker(workInProgress); + const instance: TracingMarkerInstance | null = workInProgress.stateNode; + if (instance !== null) { + popMarkerInstance(workInProgress); + } bubbleProperties(workInProgress); if ( diff --git a/packages/react-reconciler/src/ReactFiberOffscreenComponent.js b/packages/react-reconciler/src/ReactFiberOffscreenComponent.js index 00904d9fdd8ed..4fdff5c8dfb0d 100644 --- a/packages/react-reconciler/src/ReactFiberOffscreenComponent.js +++ b/packages/react-reconciler/src/ReactFiberOffscreenComponent.js @@ -8,12 +8,12 @@ */ import type {ReactNodeList, OffscreenMode} from 'shared/ReactTypes'; -import type {Fiber} from './ReactInternalTypes'; import type {Lanes} from './ReactFiberLane.old'; import type {SpawnedCachePool} from './ReactFiberCacheComponent.new'; import type { Transition, PendingSuspenseBoundaries, + TracingMarkerInstance, } from './ReactFiberTracingMarkerComponent.new'; export type OffscreenProps = {| @@ -39,7 +39,7 @@ export type OffscreenState = {| export type OffscreenQueue = {| transitions: Array | null, - tracingMarkers: Array | null, + markerInstances: Array | null, |} | null; export type OffscreenInstance = {| diff --git a/packages/react-reconciler/src/ReactFiberRoot.new.js b/packages/react-reconciler/src/ReactFiberRoot.new.js index d8ac576f4fd4c..f171ca0de3943 100644 --- a/packages/react-reconciler/src/ReactFiberRoot.new.js +++ b/packages/react-reconciler/src/ReactFiberRoot.new.js @@ -92,13 +92,13 @@ function FiberRootNode( this.hydrationCallbacks = null; } + this.incompleteTransitions = new Map(); if (enableTransitionTracing) { this.transitionCallbacks = null; const transitionLanesMap = (this.transitionLanes = []); for (let i = 0; i < TotalLanes; i++) { transitionLanesMap.push(null); } - this.incompleteTransitions = null; } if (enableProfilerTimer && enableProfilerCommitHooks) { diff --git a/packages/react-reconciler/src/ReactFiberRoot.old.js b/packages/react-reconciler/src/ReactFiberRoot.old.js index 7bf68ae5d270d..9b37cee41edab 100644 --- a/packages/react-reconciler/src/ReactFiberRoot.old.js +++ b/packages/react-reconciler/src/ReactFiberRoot.old.js @@ -92,13 +92,13 @@ function FiberRootNode( this.hydrationCallbacks = null; } + this.incompleteTransitions = new Map(); if (enableTransitionTracing) { this.transitionCallbacks = null; const transitionLanesMap = (this.transitionLanes = []); for (let i = 0; i < TotalLanes; i++) { transitionLanesMap.push(null); } - this.incompleteTransitions = null; } if (enableProfilerTimer && enableProfilerCommitHooks) { diff --git a/packages/react-reconciler/src/ReactFiberTracingMarkerComponent.new.js b/packages/react-reconciler/src/ReactFiberTracingMarkerComponent.new.js index 3a136cda686fd..0eb87bc3d6dbd 100644 --- a/packages/react-reconciler/src/ReactFiberTracingMarkerComponent.new.js +++ b/packages/react-reconciler/src/ReactFiberTracingMarkerComponent.new.js @@ -13,6 +13,7 @@ import type {StackCursor} from './ReactFiberStack.new'; import {enableTransitionTracing} from 'shared/ReactFeatureFlags'; import {createCursor, push, pop} from './ReactFiberStack.new'; +import {getWorkInProgressTransitions} from './ReactFiberWorkLoop.new'; export type SuspenseInfo = {name: string | null}; @@ -100,31 +101,77 @@ export function processTransitionCallbacks( // tracing marker can be logged as complete // This code lives separate from the ReactFiberTransition code because // we push and pop on the tracing marker, not the suspense boundary -const tracingMarkerStack: StackCursor | null> = createCursor(null); +const markerInstanceStack: StackCursor | null> = createCursor( + null, +); -export function pushTracingMarker(workInProgress: Fiber): void { +export function pushRootMarkerInstance(workInProgress: Fiber): void { if (enableTransitionTracing) { - if (tracingMarkerStack.current === null) { - push(tracingMarkerStack, [workInProgress], workInProgress); + // On the root, every transition gets mapped to it's own map of + // suspense boundaries. The transition is marked as complete when + // the suspense boundaries map is empty. We do this because every + // transition completes at different times and depends on different + // suspense boundaries to complete. We store all the transitions + // along with its map of suspense boundaries in the root incomplete + // transitions map. Each entry in this map functions like a tracing + // marker does, so we can push it onto the marker instance stack + const transitions = getWorkInProgressTransitions(); + const root = workInProgress.stateNode; + + if (transitions !== null) { + transitions.forEach(transition => { + if (!root.incompleteTransitions.has(transition)) { + root.incompleteTransitions.set(transition, { + transitions: new Set([transition]), + pendingSuspenseBoundaries: null, + }); + } + }); + } + + const markerInstances = []; + // For ever transition on the suspense boundary, we push the transition + // along with its map of pending suspense boundaries onto the marker + // instance stack. + root.incompleteTransitions.forEach(markerInstance => { + markerInstances.push(markerInstance); + }); + push(markerInstanceStack, markerInstances, workInProgress); + } +} + +export function popRootMarkerInstance(workInProgress: Fiber) { + if (enableTransitionTracing) { + pop(markerInstanceStack, workInProgress); + } +} + +export function pushMarkerInstance( + workInProgress: Fiber, + markerInstance: TracingMarkerInstance, +): void { + if (enableTransitionTracing) { + if (markerInstanceStack.current === null) { + push(markerInstanceStack, [markerInstance], workInProgress); } else { push( - tracingMarkerStack, - tracingMarkerStack.current.concat(workInProgress), + markerInstanceStack, + markerInstanceStack.current.concat(markerInstance), workInProgress, ); } } } -export function popTracingMarker(workInProgress: Fiber): void { +export function popMarkerInstance(workInProgress: Fiber): void { if (enableTransitionTracing) { - pop(tracingMarkerStack, workInProgress); + pop(markerInstanceStack, workInProgress); } } -export function getTracingMarkers(): Array | null { +export function getMarkerInstances(): Array | null { if (enableTransitionTracing) { - return tracingMarkerStack.current; + return markerInstanceStack.current; } return null; } diff --git a/packages/react-reconciler/src/ReactFiberTracingMarkerComponent.old.js b/packages/react-reconciler/src/ReactFiberTracingMarkerComponent.old.js index 5f119b3a4b66a..6f25d1f72b825 100644 --- a/packages/react-reconciler/src/ReactFiberTracingMarkerComponent.old.js +++ b/packages/react-reconciler/src/ReactFiberTracingMarkerComponent.old.js @@ -13,6 +13,7 @@ import type {StackCursor} from './ReactFiberStack.old'; import {enableTransitionTracing} from 'shared/ReactFeatureFlags'; import {createCursor, push, pop} from './ReactFiberStack.old'; +import {getWorkInProgressTransitions} from './ReactFiberWorkLoop.old'; export type SuspenseInfo = {name: string | null}; @@ -100,31 +101,77 @@ export function processTransitionCallbacks( // tracing marker can be logged as complete // This code lives separate from the ReactFiberTransition code because // we push and pop on the tracing marker, not the suspense boundary -const tracingMarkerStack: StackCursor | null> = createCursor(null); +const markerInstanceStack: StackCursor | null> = createCursor( + null, +); -export function pushTracingMarker(workInProgress: Fiber): void { +export function pushRootMarkerInstance(workInProgress: Fiber): void { if (enableTransitionTracing) { - if (tracingMarkerStack.current === null) { - push(tracingMarkerStack, [workInProgress], workInProgress); + // On the root, every transition gets mapped to it's own map of + // suspense boundaries. The transition is marked as complete when + // the suspense boundaries map is empty. We do this because every + // transition completes at different times and depends on different + // suspense boundaries to complete. We store all the transitions + // along with its map of suspense boundaries in the root incomplete + // transitions map. Each entry in this map functions like a tracing + // marker does, so we can push it onto the marker instance stack + const transitions = getWorkInProgressTransitions(); + const root = workInProgress.stateNode; + + if (transitions !== null) { + transitions.forEach(transition => { + if (!root.incompleteTransitions.has(transition)) { + root.incompleteTransitions.set(transition, { + transitions: new Set([transition]), + pendingSuspenseBoundaries: null, + }); + } + }); + } + + const markerInstances = []; + // For ever transition on the suspense boundary, we push the transition + // along with its map of pending suspense boundaries onto the marker + // instance stack. + root.incompleteTransitions.forEach(markerInstance => { + markerInstances.push(markerInstance); + }); + push(markerInstanceStack, markerInstances, workInProgress); + } +} + +export function popRootMarkerInstance(workInProgress: Fiber) { + if (enableTransitionTracing) { + pop(markerInstanceStack, workInProgress); + } +} + +export function pushMarkerInstance( + workInProgress: Fiber, + markerInstance: TracingMarkerInstance, +): void { + if (enableTransitionTracing) { + if (markerInstanceStack.current === null) { + push(markerInstanceStack, [markerInstance], workInProgress); } else { push( - tracingMarkerStack, - tracingMarkerStack.current.concat(workInProgress), + markerInstanceStack, + markerInstanceStack.current.concat(markerInstance), workInProgress, ); } } } -export function popTracingMarker(workInProgress: Fiber): void { +export function popMarkerInstance(workInProgress: Fiber): void { if (enableTransitionTracing) { - pop(tracingMarkerStack, workInProgress); + pop(markerInstanceStack, workInProgress); } } -export function getTracingMarkers(): Array | null { +export function getMarkerInstances(): Array | null { if (enableTransitionTracing) { - return tracingMarkerStack.current; + return markerInstanceStack.current; } return null; } diff --git a/packages/react-reconciler/src/ReactFiberUnwindWork.new.js b/packages/react-reconciler/src/ReactFiberUnwindWork.new.js index 6b2dca917d39b..2659a74d80fd5 100644 --- a/packages/react-reconciler/src/ReactFiberUnwindWork.new.js +++ b/packages/react-reconciler/src/ReactFiberUnwindWork.new.js @@ -12,6 +12,7 @@ import type {Fiber, FiberRoot} from './ReactInternalTypes'; import type {Lanes} from './ReactFiberLane.new'; import type {SuspenseState} from './ReactFiberSuspenseComponent.new'; import type {Cache} from './ReactFiberCacheComponent.new'; +import type {TracingMarkerInstance} from './ReactFiberTracingMarkerComponent.new'; import {resetWorkInProgressVersions as resetMutableSourceWorkInProgressVersions} from './ReactMutableSource.new'; import { @@ -52,7 +53,10 @@ import {popCacheProvider} from './ReactFiberCacheComponent.new'; import {transferActualDuration} from './ReactProfilerTimer.new'; import {popTreeContext} from './ReactFiberTreeContext.new'; import {popRootTransition, popTransition} from './ReactFiberTransition.new'; -import {popTracingMarker} from './ReactFiberTracingMarkerComponent.new'; +import { + popMarkerInstance, + popRootMarkerInstance, +} from './ReactFiberTracingMarkerComponent.new'; function unwindWork( current: Fiber | null, @@ -89,6 +93,11 @@ function unwindWork( const cache: Cache = workInProgress.memoizedState.cache; popCacheProvider(workInProgress, cache); } + + if (enableTransitionTracing) { + popRootMarkerInstance(workInProgress); + } + popRootTransition(workInProgress, root, renderLanes); popHostContainer(workInProgress); popTopLevelLegacyContextObject(workInProgress); @@ -165,7 +174,9 @@ function unwindWork( return null; case TracingMarkerComponent: if (enableTransitionTracing) { - popTracingMarker(workInProgress); + if (workInProgress.stateNode !== null) { + popMarkerInstance(workInProgress); + } } return null; default: @@ -197,6 +208,11 @@ function unwindInterruptedWork( const cache: Cache = interruptedWork.memoizedState.cache; popCacheProvider(interruptedWork, cache); } + + if (enableTransitionTracing) { + popRootMarkerInstance(interruptedWork); + } + popRootTransition(interruptedWork, root, renderLanes); popHostContainer(interruptedWork); popTopLevelLegacyContextObject(interruptedWork); @@ -233,7 +249,11 @@ function unwindInterruptedWork( break; case TracingMarkerComponent: if (enableTransitionTracing) { - popTracingMarker(interruptedWork); + const instance: TracingMarkerInstance | null = + interruptedWork.stateNode; + if (instance !== null) { + popMarkerInstance(interruptedWork); + } } break; default: diff --git a/packages/react-reconciler/src/ReactFiberUnwindWork.old.js b/packages/react-reconciler/src/ReactFiberUnwindWork.old.js index ba2e202dd552e..f56c8ce5d2893 100644 --- a/packages/react-reconciler/src/ReactFiberUnwindWork.old.js +++ b/packages/react-reconciler/src/ReactFiberUnwindWork.old.js @@ -12,6 +12,7 @@ import type {Fiber, FiberRoot} from './ReactInternalTypes'; import type {Lanes} from './ReactFiberLane.old'; import type {SuspenseState} from './ReactFiberSuspenseComponent.old'; import type {Cache} from './ReactFiberCacheComponent.old'; +import type {TracingMarkerInstance} from './ReactFiberTracingMarkerComponent.old'; import {resetWorkInProgressVersions as resetMutableSourceWorkInProgressVersions} from './ReactMutableSource.old'; import { @@ -49,7 +50,10 @@ import {popCacheProvider} from './ReactFiberCacheComponent.old'; import {transferActualDuration} from './ReactProfilerTimer.old'; import {popTreeContext} from './ReactFiberTreeContext.old'; import {popRootTransition, popTransition} from './ReactFiberTransition.old'; -import {popTracingMarker} from './ReactFiberTracingMarkerComponent.old'; +import { + popMarkerInstance, + popRootMarkerInstance, +} from './ReactFiberTracingMarkerComponent.old'; function unwindWork( current: Fiber | null, @@ -86,6 +90,11 @@ function unwindWork( const cache: Cache = workInProgress.memoizedState.cache; popCacheProvider(workInProgress, cache); } + + if (enableTransitionTracing) { + popRootMarkerInstance(workInProgress); + } + popRootTransition(workInProgress, root, renderLanes); popHostContainer(workInProgress); popTopLevelLegacyContextObject(workInProgress); @@ -162,7 +171,9 @@ function unwindWork( return null; case TracingMarkerComponent: if (enableTransitionTracing) { - popTracingMarker(workInProgress); + if (workInProgress.stateNode !== null) { + popMarkerInstance(workInProgress); + } } return null; default: @@ -194,6 +205,11 @@ function unwindInterruptedWork( const cache: Cache = interruptedWork.memoizedState.cache; popCacheProvider(interruptedWork, cache); } + + if (enableTransitionTracing) { + popRootMarkerInstance(interruptedWork); + } + popRootTransition(interruptedWork, root, renderLanes); popHostContainer(interruptedWork); popTopLevelLegacyContextObject(interruptedWork); @@ -230,7 +246,11 @@ function unwindInterruptedWork( break; case TracingMarkerComponent: if (enableTransitionTracing) { - popTracingMarker(interruptedWork); + const instance: TracingMarkerInstance | null = + interruptedWork.stateNode; + if (instance !== null) { + popMarkerInstance(interruptedWork); + } } break; default: diff --git a/packages/react-reconciler/src/ReactInternalTypes.js b/packages/react-reconciler/src/ReactInternalTypes.js index 0f6e9bcaf79ff..8aad50f4bfc6b 100644 --- a/packages/react-reconciler/src/ReactInternalTypes.js +++ b/packages/react-reconciler/src/ReactInternalTypes.js @@ -335,7 +335,7 @@ type TransitionTracingOnlyFiberRootProperties = {| // are considered complete when the pending suspense boundaries set is // empty. We can represent this as a Map of transitions to suspense // boundary sets - incompleteTransitions: Map | null, + incompleteTransitions: Map, |}; // Exported FiberRoot type includes all properties,