Skip to content

Merge ViewTransition layout/onLayout props into update/onUpdate #32723

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 4 additions & 8 deletions packages/react-reconciler/src/ReactFiberApplyGesture.js
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ function applyNestedViewTransition(child: Fiber): void {
const name = getViewTransitionName(props, state);
const className: ?string = getViewTransitionClassName(
props.className,
props.layout,
props.update,
);
if (className !== 'none') {
const clones = state.clones;
Expand All @@ -335,17 +335,13 @@ function applyUpdateViewTransition(current: Fiber, finishedWork: Fiber): void {
// we would use. However, since this animation is going in reverse we actually
// want the props from "current" since that's the class that would've won if
// it was the normal direction. To preserve the same effect in either direction.
let className: ?string = getViewTransitionClassName(
const className: ?string = getViewTransitionClassName(
newProps.className,
newProps.update,
);
if (className === 'none') {
className = getViewTransitionClassName(newProps.className, newProps.layout);
if (className === 'none') {
// If both update and layout are both "none" then we don't have to
// apply a name. Since we won't animate this boundary.
return;
}
// If update is "none" then we don't have to apply a name. Since we won't animate this boundary.
return;
}
const clones = state.clones;
// If there are no clones at this point, that should mean that there are no
Expand Down
107 changes: 11 additions & 96 deletions packages/react-reconciler/src/ReactFiberCommitViewTransitions.js
Original file line number Diff line number Diff line change
Expand Up @@ -469,17 +469,13 @@ export function commitBeforeUpdateViewTransition(
// For example, if update="foo" layout="none" and it turns out this was
// a layout only change, then the "foo" class will be applied even though
// it was not actually an update. Which is a bug.
let className: ?string = getViewTransitionClassName(
const className: ?string = getViewTransitionClassName(
newProps.className,
newProps.update,
);
if (className === 'none') {
className = getViewTransitionClassName(newProps.className, newProps.layout);
if (className === 'none') {
// If both update and layout are both "none" then we don't have to
// apply a name. Since we won't animate this boundary.
return;
}
// If update is "none" then we don't have to apply a name. Since we won't animate this boundary.
return;
}
applyViewTransitionToHostInstances(
current.child,
Expand All @@ -500,7 +496,7 @@ export function commitNestedViewTransitions(changedParent: Fiber): void {
const name = getViewTransitionName(props, child.stateNode);
const className: ?string = getViewTransitionClassName(
props.className,
props.layout,
props.update,
);
if (className !== 'none') {
applyViewTransitionToHostInstances(
Expand Down Expand Up @@ -590,61 +586,6 @@ export function restoreNestedViewTransitions(changedParent: Fiber): void {
}
}

export function cancelViewTransitionHostInstances(
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This helper was only used when layout was "none" but update was not. If both were "none" we don't apply the names in the first place. Which is what happens if just update is "none" now and this case is not expressible.

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) {
return;
}
while (child !== null) {
if (child.tag === HostComponent) {
const instance: Instance = child.stateNode;
if (viewTransitionCancelableChildren === null) {
viewTransitionCancelableChildren = [];
}
viewTransitionCancelableChildren.push(
instance,
oldName,
child.memoizedProps,
);
viewTransitionHostInstanceIdx++;
} else if (
child.tag === OffscreenComponent &&
child.memoizedState !== null
) {
// Skip any hidden subtrees. They were or are effectively not there.
} else if (
child.tag === ViewTransitionComponent &&
stopAtNestedViewTransitions
) {
// Skip any nested view transitions for updates since in that case the
// inner most one is the one that handles the update.
} else {
cancelViewTransitionHostInstancesRecursive(
child.child,
oldName,
stopAtNestedViewTransitions,
);
}
child = child.sibling;
}
}

export function measureViewTransitionHostInstances(
parentViewTransition: Fiber,
child: null | Fiber,
Expand Down Expand Up @@ -792,40 +733,14 @@ export function measureUpdateViewTransition(
const state: ViewTransitionState = newFiber.stateNode;
const newName = getViewTransitionName(props, state);
const oldName = getViewTransitionName(oldFiber.memoizedProps, state);
const updateClassName: ?string = getViewTransitionClassName(
// Whether it ends up having been updated or relayout we apply the update class name.
const className: ?string = getViewTransitionClassName(
props.className,
props.update,
);
const layoutClassName: ?string = getViewTransitionClassName(
props.className,
props.layout,
);
let className: ?string;
if (updateClassName === 'none') {
if (layoutClassName === 'none') {
// If both update and layout class name were none, then we didn't apply any
// names in the before update phase so we shouldn't now neither.
return false;
}
// We don't care if this is mutated or children layout changed, but we still
// measure each instance to see if it moved and therefore should apply layout.
finishedWork.flags &= ~Update;
className = layoutClassName;
} else if ((finishedWork.flags & Update) !== NoFlags) {
// It was updated and we have an appropriate class name to apply.
className = updateClassName;
} else {
if (layoutClassName === 'none') {
// If we did not update, then all changes are considered a layout. We'll
// attempt to cancel.
// This should use the Fiber that got names applied in the snapshot phase
// since those are the ones we're trying to cancel.
cancelViewTransitionHostInstances(oldFiber.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;
if (className === 'none') {
// If update is "none" then we don't have to apply a name. Since we won't animate this boundary.
return false;
}
// If nothing changed due to a mutation, or children changing size
// and the measurements end up unchanged, we should restore it to not animate.
Expand Down Expand Up @@ -873,7 +788,7 @@ export function measureNestedViewTransitions(
const name = getViewTransitionName(props, state);
const className: ?string = getViewTransitionClassName(
props.className,
props.layout,
props.update,
);
let previousMeasurements: null | Array<InstanceMeasurement>;
if (gesture) {
Expand Down Expand Up @@ -902,7 +817,7 @@ export function measureNestedViewTransitions(
if (gesture) {
// TODO: Schedule gesture events.
} else {
scheduleViewTransitionEvent(child, props.onLayout);
scheduleViewTransitionEvent(child, props.onUpdate);
}
}
} else if ((child.subtreeFlags & ViewTransitionStatic) !== NoFlags) {
Expand Down
9 changes: 1 addition & 8 deletions packages/react-reconciler/src/ReactFiberCommitWork.js
Original file line number Diff line number Diff line change
Expand Up @@ -2517,8 +2517,6 @@ function commitAfterMutationEffectsOnFiber(
break;
}
case ViewTransitionComponent: {
const wasMutated = (finishedWork.flags & Update) !== NoFlags;

const prevContextChanged = viewTransitionContextChanged;
const prevCancelableChildren = pushViewTransitionCancelableScope();
viewTransitionContextChanged = false;
Expand Down Expand Up @@ -2554,12 +2552,7 @@ function commitAfterMutationEffectsOnFiber(
// then we should probably issue an event since this instance is part of it.
} else {
const props: ViewTransitionProps = finishedWork.memoizedProps;
scheduleViewTransitionEvent(
finishedWork,
wasMutated || viewTransitionContextChanged
? props.onUpdate
: props.onLayout,
);
scheduleViewTransitionEvent(finishedWork, props.onUpdate);

// If this boundary did update, we cannot cancel its children so those are dropped.
popViewTransitionCancelableScope(prevCancelableChildren);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,10 @@ export type ViewTransitionProps = {
className?: ViewTransitionClass,
enter?: ViewTransitionClass,
exit?: ViewTransitionClass,
layout?: ViewTransitionClass,
share?: ViewTransitionClass,
update?: ViewTransitionClass,
onEnter?: (instance: ViewTransitionInstance, types: Array<string>) => void,
onExit?: (instance: ViewTransitionInstance, types: Array<string>) => void,
onLayout?: (instance: ViewTransitionInstance, types: Array<string>) => void,
onShare?: (instance: ViewTransitionInstance, types: Array<string>) => void,
onUpdate?: (instance: ViewTransitionInstance, types: Array<string>) => void,
};
Expand Down
Loading