Skip to content

Commit d0e69fe

Browse files
committed
Don't pair up named transitions if one side of out of the viewport
This avoids things flying in, onto, or across the screen unexpectedly. Unfortunately if it goes from onscreen to offscreen we have no way to cancel the old state, so that appears as an exit even though it should've just painted on top of its deleted parent if we had known.
1 parent 6bc9cc9 commit d0e69fe

File tree

1 file changed

+27
-6
lines changed

1 file changed

+27
-6
lines changed

packages/react-reconciler/src/ReactFiberCommitWork.js

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -661,12 +661,21 @@ function commitAppearingPairViewTransitions(placement: Fiber): void {
661661
// We found a new appearing view transition with the same name as this deletion.
662662
// We'll transition between them.
663663
viewTransitionHostInstanceIdx = 0;
664-
applyViewTransitionToHostInstances(
664+
const inViewport = applyViewTransitionToHostInstances(
665665
child.child,
666666
props.name,
667667
null,
668668
false,
669669
);
670+
if (!inViewport) {
671+
// This boundary is exiting within the viewport but is going to leave the viewport.
672+
// Instead, we treat this as an exit of the previous entry by reverting the new name.
673+
// Ideally we could undo the old transition but it's now too late. It's also on its
674+
// on snapshot. We have know was for it to paint onto the original group.
675+
// TODO: This will lead to things unexpectedly having exit animations that normally
676+
// wouldn't happen. Consider if we should just let this fly off the screen instead.
677+
restoreViewTransitionOnHostInstances(child.child, false);
678+
}
670679
}
671680
}
672681
commitAppearingPairViewTransitions(child);
@@ -733,13 +742,25 @@ function commitDeletedPairViewTransitions(
733742
if (name != null && name !== 'auto') {
734743
const pair = appearingViewTransitions.get(name);
735744
if (pair !== undefined) {
736-
const oldinstance: ViewTransitionInstance = child.stateNode;
737-
const newInstance: ViewTransitionInstance = pair;
738-
newInstance.paired = oldinstance;
739745
// We found a new appearing view transition with the same name as this deletion.
740-
// We'll transition between them.
741746
viewTransitionHostInstanceIdx = 0;
742-
applyViewTransitionToHostInstances(child.child, name, null, false);
747+
const inViewport = applyViewTransitionToHostInstances(
748+
child.child,
749+
name,
750+
null,
751+
false,
752+
);
753+
if (!inViewport) {
754+
// This boundary is not in the viewport so we won't treat it as a matched pair.
755+
// Revert the transition names. This avoids it flying onto the screen which can
756+
// be disruptive and doesn't really preserve any continuity anyway.
757+
restoreViewTransitionOnHostInstances(child.child, false);
758+
} else {
759+
// We'll transition between them.
760+
const oldinstance: ViewTransitionInstance = child.stateNode;
761+
const newInstance: ViewTransitionInstance = pair;
762+
newInstance.paired = oldinstance;
763+
}
743764
// Delete the entry so that we know when we've found all of them
744765
// and can stop searching (size reaches zero).
745766
appearingViewTransitions.delete(name);

0 commit comments

Comments
 (0)