diff --git a/packages/react-reconciler/src/ReactFiberCommitViewTransitions.js b/packages/react-reconciler/src/ReactFiberCommitViewTransitions.js index 22b9d11ac67d2..ca3df168dc30a 100644 --- a/packages/react-reconciler/src/ReactFiberCommitViewTransitions.js +++ b/packages/react-reconciler/src/ReactFiberCommitViewTransitions.js @@ -71,15 +71,40 @@ export let viewTransitionCancelableChildren: null | Array< Instance | string | Props, > = null; // tupled array where each entry is [instance: Instance, oldName: string, props: Props] -export function setViewTransitionCancelableChildren( - children: null | Array, +export function pushViewTransitionCancelableScope(): null | Array< + Instance | string | Props, +> { + const prevChildren = viewTransitionCancelableChildren; + viewTransitionCancelableChildren = null; + return prevChildren; +} + +export function popViewTransitionCancelableScope( + prevChildren: null | Array, ): void { - viewTransitionCancelableChildren = children; + viewTransitionCancelableChildren = prevChildren; } let viewTransitionHostInstanceIdx = 0; -function applyViewTransitionToHostInstances( +export function applyViewTransitionToHostInstances( + child: null | Fiber, + name: string, + className: ?string, + collectMeasurements: null | Array, + stopAtNestedViewTransitions: boolean, +): boolean { + viewTransitionHostInstanceIdx = 0; + return applyViewTransitionToHostInstancesRecursive( + child, + name, + className, + collectMeasurements, + stopAtNestedViewTransitions, + ); +} + +function applyViewTransitionToHostInstancesRecursive( child: null | Fiber, name: string, className: ?string, @@ -128,7 +153,7 @@ function applyViewTransitionToHostInstances( // inner most one is the one that handles the update. } else { if ( - applyViewTransitionToHostInstances( + applyViewTransitionToHostInstancesRecursive( child.child, name, className, @@ -207,7 +232,6 @@ function commitAppearingPairViewTransitions(placement: Fiber): void { if (className !== 'none') { // We found a new appearing view transition with the same name as this deletion. // We'll transition between them. - viewTransitionHostInstanceIdx = 0; const inViewport = applyViewTransitionToHostInstances( child.child, name, @@ -242,7 +266,6 @@ export function commitEnterViewTransitions(placement: Fiber): void { state.paired ? props.share : props.enter, ); if (className !== 'none') { - viewTransitionHostInstanceIdx = 0; const inViewport = applyViewTransitionToHostInstances( placement.child, name, @@ -310,7 +333,6 @@ function commitDeletedPairViewTransitions(deletion: Fiber): void { ); if (className !== 'none') { // We found a new appearing view transition with the same name as this deletion. - viewTransitionHostInstanceIdx = 0; const inViewport = applyViewTransitionToHostInstances( child.child, name, @@ -361,7 +383,6 @@ export function commitExitViewTransitions(deletion: Fiber): void { pair !== undefined ? props.share : props.exit, ); if (className !== 'none') { - viewTransitionHostInstanceIdx = 0; const inViewport = applyViewTransitionToHostInstances( deletion.child, name, @@ -449,7 +470,6 @@ export function commitBeforeUpdateViewTransition( return; } } - viewTransitionHostInstanceIdx = 0; applyViewTransitionToHostInstances( current.child, oldName, @@ -472,7 +492,6 @@ export function commitNestedViewTransitions(changedParent: Fiber): void { props.layout, ); if (className !== 'none') { - viewTransitionHostInstanceIdx = 0; applyViewTransitionToHostInstances( child.child, name, @@ -570,9 +589,22 @@ export function restoreNestedViewTransitions(changedParent: Fiber): void { } } -function cancelViewTransitionHostInstances( - currentViewTransition: Fiber, +export function cancelViewTransitionHostInstances( child: null | Fiber, + oldName: string, + stopAtNestedViewTransitions: boolean, +): void { + viewTransitionHostInstanceIdx = 0; + cancelViewTransitionHostInstancesRecursive( + child, + oldName, + stopAtNestedViewTransitions, + ); +} + +function cancelViewTransitionHostInstancesRecursive( + child: null | Fiber, + oldName: string, stopAtNestedViewTransitions: boolean, ): void { if (!supportsMutation) { @@ -581,10 +613,6 @@ function cancelViewTransitionHostInstances( while (child !== null) { if (child.tag === HostComponent) { const instance: Instance = child.stateNode; - const oldName = getViewTransitionName( - currentViewTransition.memoizedProps, - currentViewTransition.stateNode, - ); if (viewTransitionCancelableChildren === null) { viewTransitionCancelableChildren = []; } @@ -606,9 +634,9 @@ function cancelViewTransitionHostInstances( // Skip any nested view transitions for updates since in that case the // inner most one is the one that handles the update. } else { - cancelViewTransitionHostInstances( - currentViewTransition, + cancelViewTransitionHostInstancesRecursive( child.child, + oldName, stopAtNestedViewTransitions, ); } @@ -616,11 +644,32 @@ function cancelViewTransitionHostInstances( } } -function measureViewTransitionHostInstances( - currentViewTransition: Fiber, +export function measureViewTransitionHostInstances( parentViewTransition: Fiber, child: null | Fiber, - name: string, + newName: string, + oldName: string, + className: ?string, + previousMeasurements: null | Array, + stopAtNestedViewTransitions: boolean, +): boolean { + viewTransitionHostInstanceIdx = 0; + return measureViewTransitionHostInstancesRecursive( + parentViewTransition, + child, + newName, + oldName, + className, + previousMeasurements, + stopAtNestedViewTransitions, + ); +} + +function measureViewTransitionHostInstancesRecursive( + parentViewTransition: Fiber, + child: null | Fiber, + newName: string, + oldName: string, className: ?string, previousMeasurements: null | Array, stopAtNestedViewTransitions: boolean, @@ -671,10 +720,10 @@ function measureViewTransitionHostInstances( applyViewTransitionName( instance, viewTransitionHostInstanceIdx === 0 - ? name + ? newName : // If we have multiple Host Instances below, we add a suffix to the name to give // each one a unique name. - name + '_' + viewTransitionHostInstanceIdx, + newName + '_' + viewTransitionHostInstanceIdx, className, ); } @@ -684,10 +733,6 @@ function measureViewTransitionHostInstances( // animating it. However, in the current model this only works if the parent also // doesn't animate. So we have to queue these and wait until we complete the parent // to cancel them. - const oldName = getViewTransitionName( - currentViewTransition.memoizedProps, - currentViewTransition.stateNode, - ); if (viewTransitionCancelableChildren === null) { viewTransitionCancelableChildren = []; } @@ -713,11 +758,11 @@ function measureViewTransitionHostInstances( parentViewTransition.flags |= child.flags & AffectedParentLayout; } else { if ( - measureViewTransitionHostInstances( - currentViewTransition, + measureViewTransitionHostInstancesRecursive( parentViewTransition, child.child, - name, + newName, + oldName, className, previousMeasurements, stopAtNestedViewTransitions, @@ -736,6 +781,11 @@ export function measureUpdateViewTransition( finishedWork: Fiber, ): boolean { const props: ViewTransitionProps = finishedWork.memoizedProps; + const newName = getViewTransitionName(props, finishedWork.stateNode); + const oldName = getViewTransitionName( + current.memoizedProps, + current.stateNode, + ); const updateClassName: ?string = getViewTransitionClassName( props.className, props.update, @@ -762,24 +812,21 @@ export function measureUpdateViewTransition( if (layoutClassName === 'none') { // If we did not update, then all changes are considered a layout. We'll // attempt to cancel. - viewTransitionHostInstanceIdx = 0; - cancelViewTransitionHostInstances(current, finishedWork.child, true); + cancelViewTransitionHostInstances(finishedWork.child, oldName, true); return false; } // We didn't update but we might still apply layout so we measure each // instance to see if it moved or resized. className = layoutClassName; } - const name = getViewTransitionName(props, finishedWork.stateNode); // If nothing changed due to a mutation, or children changing size // and the measurements end up unchanged, we should restore it to not animate. - viewTransitionHostInstanceIdx = 0; const previousMeasurements = current.memoizedState; const inViewport = measureViewTransitionHostInstances( - current, finishedWork, finishedWork.child, - name, + newName, + oldName, className, previousMeasurements, true, @@ -799,29 +846,25 @@ export function measureNestedViewTransitions(changedParent: Fiber): void { let child = changedParent.child; while (child !== null) { if (child.tag === ViewTransitionComponent) { - const current = child.alternate; - if (current !== null) { - const props: ViewTransitionProps = child.memoizedProps; - const name = getViewTransitionName(props, child.stateNode); - const className: ?string = getViewTransitionClassName( - props.className, - props.layout, - ); - viewTransitionHostInstanceIdx = 0; - const inViewport = measureViewTransitionHostInstances( - current, - child, - child.child, - name, - className, - child.memoizedState, - false, - ); - if ((child.flags & Update) === NoFlags || !inViewport) { - // Nothing changed. - } else { - scheduleViewTransitionEvent(child, props.onLayout); - } + const props: ViewTransitionProps = child.memoizedProps; + const name = getViewTransitionName(props, child.stateNode); + const className: ?string = getViewTransitionClassName( + props.className, + props.layout, + ); + const inViewport = measureViewTransitionHostInstances( + child, + child.child, + name, + name, // Since this is unchanged, new and old name is the same. + className, + child.memoizedState, + false, + ); + if ((child.flags & Update) === NoFlags || !inViewport) { + // Nothing changed. + } else { + scheduleViewTransitionEvent(child, props.onLayout); } } else if ((child.subtreeFlags & ViewTransitionStatic) !== NoFlags) { measureNestedViewTransitions(child); diff --git a/packages/react-reconciler/src/ReactFiberCommitWork.js b/packages/react-reconciler/src/ReactFiberCommitWork.js index 8b82e6a7e713c..168a5d80f65b3 100644 --- a/packages/react-reconciler/src/ReactFiberCommitWork.js +++ b/packages/react-reconciler/src/ReactFiberCommitWork.js @@ -255,7 +255,8 @@ import { resetAppearingViewTransitions, trackAppearingViewTransition, viewTransitionCancelableChildren, - setViewTransitionCancelableChildren, + pushViewTransitionCancelableScope, + popViewTransitionCancelableScope, } from './ReactFiberCommitViewTransitions'; import { viewTransitionMutationContext, @@ -2475,14 +2476,14 @@ function commitAfterMutationEffectsOnFiber( switch (finishedWork.tag) { case HostRoot: { viewTransitionContextChanged = false; - setViewTransitionCancelableChildren(null); + pushViewTransitionCancelableScope(); recursivelyTraverseAfterMutationEffects(root, finishedWork, lanes); if (!viewTransitionContextChanged) { // If we didn't leak any resizing out to the root, we don't have to transition // the root itself. This means that we can now safely cancel any cancellations // that bubbled all the way up. const cancelableChildren = viewTransitionCancelableChildren; - setViewTransitionCancelableChildren(null); + popViewTransitionCancelableScope(null); if (cancelableChildren !== null) { for (let i = 0; i < cancelableChildren.length; i += 3) { cancelViewTransitionName( @@ -2533,9 +2534,8 @@ function commitAfterMutationEffectsOnFiber( const wasMutated = (finishedWork.flags & Update) !== NoFlags; const prevContextChanged = viewTransitionContextChanged; - const prevCancelableChildren = viewTransitionCancelableChildren; + const prevCancelableChildren = pushViewTransitionCancelableScope(); viewTransitionContextChanged = false; - setViewTransitionCancelableChildren(null); recursivelyTraverseAfterMutationEffects(root, finishedWork, lanes); if (viewTransitionContextChanged) { @@ -2558,7 +2558,7 @@ function commitAfterMutationEffectsOnFiber( prevCancelableChildren, viewTransitionCancelableChildren, ); - setViewTransitionCancelableChildren(prevCancelableChildren); + popViewTransitionCancelableScope(prevCancelableChildren); } // TODO: If this doesn't end up canceled, because a parent animates, // then we should probably issue an event since this instance is part of it. @@ -2572,7 +2572,7 @@ function commitAfterMutationEffectsOnFiber( ); // If this boundary did update, we cannot cancel its children so those are dropped. - setViewTransitionCancelableChildren(prevCancelableChildren); + popViewTransitionCancelableScope(prevCancelableChildren); } if ((finishedWork.flags & AffectedParentLayout) !== NoFlags) {