diff --git a/packages/react-reconciler/src/ReactFiber.old.js b/packages/react-reconciler/src/ReactFiber.old.js index 0a29c4dce782f..30f7b587fb021 100644 --- a/packages/react-reconciler/src/ReactFiber.old.js +++ b/packages/react-reconciler/src/ReactFiber.old.js @@ -772,6 +772,8 @@ export function createFiberFromTracingMarker( const tracingMarkerInstance: TracingMarkerInstance = { transitions: null, pendingBoundaries: null, + deletions: null, + name: pendingProps.name, }; fiber.stateNode = tracingMarkerInstance; return fiber; diff --git a/packages/react-reconciler/src/ReactFiberBeginWork.old.js b/packages/react-reconciler/src/ReactFiberBeginWork.old.js index 1fa498e29a929..ab802a9dbbc8f 100644 --- a/packages/react-reconciler/src/ReactFiberBeginWork.old.js +++ b/packages/react-reconciler/src/ReactFiberBeginWork.old.js @@ -979,6 +979,7 @@ function updateTracingMarkerComponent( transitions: new Set(currentTransitions), pendingBoundaries: new Map(), name: workInProgress.pendingProps.name, + deletions: null, }; workInProgress.stateNode = markerInstance; } diff --git a/packages/react-reconciler/src/ReactFiberCommitWork.old.js b/packages/react-reconciler/src/ReactFiberCommitWork.old.js index b0e16e8ab2aeb..20eaa3ccf1041 100644 --- a/packages/react-reconciler/src/ReactFiberCommitWork.old.js +++ b/packages/react-reconciler/src/ReactFiberCommitWork.old.js @@ -146,6 +146,7 @@ import { addTransitionProgressCallbackToPendingTransition, addTransitionCompleteCallbackToPendingTransition, addMarkerProgressCallbackToPendingTransition, + addMarkerIncompleteCallbackToPendingTransition, addMarkerCompleteCallbackToPendingTransition, setIsRunningInsertionEffect, } from './ReactFiberWorkLoop.old'; @@ -3018,19 +3019,51 @@ function commitTracingMarkerPassiveMountEffect(finishedWork: Fiber) { // Get the transitions that were initiatized during the render // and add a start transition callback for each of them const instance = finishedWork.stateNode; - if ( - instance.transitions !== null && - (instance.pendingBoundaries === null || - instance.pendingBoundaries.size === 0) - ) { - instance.transitions.forEach(transition => { - addMarkerCompleteCallbackToPendingTransition( - finishedWork.memoizedProps.name, - instance.transitions, - ); - }); - instance.transitions = null; - instance.pendingBoundaries = null; + if (instance.transitions !== null) { + if (finishedWork.alternate !== null) { + const prevName = finishedWork.alternate.memoizedProps.name; + const nextName = finishedWork.memoizedProps.name; + + // The transition should be marked as incomplete if the name changed + if (prevName !== nextName) { + if (!instance.deletions) { + instance.deletions = []; + + addMarkerIncompleteCallbackToPendingTransition( + prevName, + instance.transitions, + instance.deletions, + ); + } + + const deletion = { + type: 'marker', + name: prevName, + newName: nextName, + // we'll filter the transitions that need to have this deletion + // during the callback stage + transitions: instance.transitions, + }; + + instance.deletions.push(deletion); + } + } + + if ( + instance.transitions !== null && + (instance.pendingBoundaries === null || + instance.pendingBoundaries.size === 0) + ) { + if (instance.deletions === null) { + addMarkerCompleteCallbackToPendingTransition( + finishedWork.memoizedProps.name, + instance.transitions, + ); + } + instance.transitions = null; + instance.pendingBoundaries = null; + instance.deletions = null; + } } } @@ -3146,7 +3179,9 @@ function commitPassiveMountOnFiber( incompleteTransitions.forEach((markerInstance, transition) => { const pendingBoundaries = markerInstance.pendingBoundaries; if (pendingBoundaries === null || pendingBoundaries.size === 0) { - addTransitionCompleteCallbackToPendingTransition(transition); + if (markerInstance.deletions === null) { + addTransitionCompleteCallbackToPendingTransition(transition); + } incompleteTransitions.delete(transition); } }); diff --git a/packages/react-reconciler/src/ReactFiberCompleteWork.old.js b/packages/react-reconciler/src/ReactFiberCompleteWork.old.js index 1ff1a5173ec10..03186a83a502e 100644 --- a/packages/react-reconciler/src/ReactFiberCompleteWork.old.js +++ b/packages/react-reconciler/src/ReactFiberCompleteWork.old.js @@ -1590,8 +1590,11 @@ function completeWork( } bubbleProperties(workInProgress); + const prevName = current !== null ? current.memoizedProps.name : null; + const nextName = workInProgress.memoizedProps.name; if ( current === null || + prevName !== nextName || (workInProgress.subtreeFlags & Visibility) !== NoFlags ) { // If any of our suspense children toggle visibility, this means that diff --git a/packages/react-reconciler/src/ReactFiberTracingMarkerComponent.old.js b/packages/react-reconciler/src/ReactFiberTracingMarkerComponent.old.js index ddd8289d8a4bc..873a223ec3425 100644 --- a/packages/react-reconciler/src/ReactFiberTracingMarkerComponent.old.js +++ b/packages/react-reconciler/src/ReactFiberTracingMarkerComponent.old.js @@ -21,7 +21,14 @@ export type PendingTransitionCallbacks = { transitionStart: Array | null, transitionProgress: Map | null, transitionComplete: Array | null, - markerProgress: Map | null, + markerProgress: Map< + string, + {pendingBoundaries: PendingBoundaries, transitions: Set}, + > | null, + markerIncomplete: Map< + string, + {deletions: Array, transitions: Set}, + > | null, markerComplete: Map> | null, }; @@ -39,7 +46,16 @@ export type BatchConfigTransition = { export type TracingMarkerInstance = {| pendingBoundaries: PendingBoundaries | null, transitions: Set | null, + deletions: Array | null, + name: string | null, +|}; + +export type TransitionDeletion = {| + type: 'error' | 'unknown' | 'marker' | 'suspense', name?: string, + newName?: string, + endTime: number, + transitions: Set, |}; export type PendingBoundaries = Map; @@ -64,6 +80,7 @@ export function processTransitionCallbacks( if (onMarkerProgress != null && markerProgress !== null) { markerProgress.forEach((markerInstance, markerName) => { if (markerInstance.transitions !== null) { + // TODO: Clone the suspense object so users can't modify it const pending = markerInstance.pendingBoundaries !== null ? Array.from(markerInstance.pendingBoundaries.values()) @@ -96,6 +113,30 @@ export function processTransitionCallbacks( }); } + const markerIncomplete = pendingTransitions.markerIncomplete; + const onMarkerIncomplete = callbacks.onMarkerIncomplete; + if (onMarkerIncomplete != null && markerIncomplete !== null) { + markerIncomplete.forEach(({transitions, deletions}, markerName) => { + transitions.forEach(transition => { + const filteredDeletions = []; + deletions.forEach(deletion => { + if (deletion.transitions.has(transition)) { + const filteredDeletion = getFilteredDeletion(deletion, endTime); + if (filteredDeletion !== null) { + filteredDeletions.push(filteredDeletion); + } + } + }); + onMarkerIncomplete( + transition.name, + markerName, + transition.startTime, + filteredDeletions, + ); + }); + }); + } + const transitionProgress = pendingTransitions.transitionProgress; const onTransitionProgress = callbacks.onTransitionProgress; if (onTransitionProgress != null && transitionProgress !== null) { @@ -120,6 +161,28 @@ export function processTransitionCallbacks( } } +function getFilteredDeletion(deletion: TransitionDeletion, endTime: number) { + switch (deletion.type) { + case 'marker': { + return deletion.newName + ? { + type: deletion.type, + name: deletion.name, + newName: deletion.newName, + endTime, + } + : { + type: deletion.type, + name: deletion.name, + endTime, + }; + } + default: { + return null; + } + } +} + // For every tracing marker, store a pointer to it. We will later access it // to get the set of suspense boundaries that need to resolve before the // tracing marker can be logged as complete @@ -148,6 +211,8 @@ export function pushRootMarkerInstance(workInProgress: Fiber): void { const markerInstance: TracingMarkerInstance = { transitions: new Set([transition]), pendingBoundaries: null, + deletions: null, + name: null, }; root.incompleteTransitions.set(transition, markerInstance); } diff --git a/packages/react-reconciler/src/ReactFiberWorkLoop.old.js b/packages/react-reconciler/src/ReactFiberWorkLoop.old.js index 4b2e523d40a21..dfeef73533d35 100644 --- a/packages/react-reconciler/src/ReactFiberWorkLoop.old.js +++ b/packages/react-reconciler/src/ReactFiberWorkLoop.old.js @@ -18,6 +18,7 @@ import type { PendingTransitionCallbacks, PendingBoundaries, Transition, + TransitionDeletion, } from './ReactFiberTracingMarkerComponent.old'; import type {OffscreenInstance} from './ReactFiberOffscreenComponent'; @@ -342,6 +343,7 @@ export function addTransitionStartCallbackToPendingTransition( transitionProgress: null, transitionComplete: null, markerProgress: null, + markerIncomplete: null, markerComplete: null, }; } @@ -357,7 +359,7 @@ export function addTransitionStartCallbackToPendingTransition( export function addMarkerProgressCallbackToPendingTransition( markerName: string, transitions: Set, - pendingBoundaries: PendingBoundaries | null, + pendingBoundaries: PendingBoundaries, ) { if (enableTransitionTracing) { if (currentPendingTransitionCallbacks === null) { @@ -366,6 +368,7 @@ export function addMarkerProgressCallbackToPendingTransition( transitionProgress: null, transitionComplete: null, markerProgress: new Map(), + markerIncomplete: null, markerComplete: null, }; } @@ -381,6 +384,34 @@ export function addMarkerProgressCallbackToPendingTransition( } } +export function addMarkerIncompleteCallbackToPendingTransition( + markerName: string, + transitions: Set, + deletions: Array, +) { + if (enableTransitionTracing) { + if (currentPendingTransitionCallbacks === null) { + currentPendingTransitionCallbacks = { + transitionStart: null, + transitionProgress: null, + transitionComplete: null, + markerProgress: null, + markerIncomplete: new Map(), + markerComplete: null, + }; + } + + if (currentPendingTransitionCallbacks.markerIncomplete === null) { + currentPendingTransitionCallbacks.markerIncomplete = new Map(); + } + + currentPendingTransitionCallbacks.markerIncomplete.set(markerName, { + transitions, + deletions, + }); + } +} + export function addMarkerCompleteCallbackToPendingTransition( markerName: string, transitions: Set, @@ -392,6 +423,7 @@ export function addMarkerCompleteCallbackToPendingTransition( transitionProgress: null, transitionComplete: null, markerProgress: null, + markerIncomplete: null, markerComplete: new Map(), }; } @@ -418,6 +450,7 @@ export function addTransitionProgressCallbackToPendingTransition( transitionProgress: new Map(), transitionComplete: null, markerProgress: null, + markerIncomplete: null, markerComplete: null, }; } @@ -443,6 +476,7 @@ export function addTransitionCompleteCallbackToPendingTransition( transitionProgress: null, transitionComplete: [], markerProgress: null, + markerIncomplete: null, markerComplete: null, }; }