Mark shouldStartViewTransition as true when there's an enter animation (#32764)
Typically we mark the name of things that might animate in the snapshot phase. At the same time we track that should call startViewTransition too. However, we don't do this for "enter" since they're only marked later. Leading to having just an "enter" not to animate unless there's at least another update too. This tracks if there's a ViewTransitionComponent in the tree that enters. Luckily we know that from the static flag so we don't have to traverse it.
Sebastian Markbåge committed
Mar 26, 2025 at 18:12 UTC
4280563b04898baad423dc7d0f8b0dfea3b1797a
2 files changed
+22
-1
packages/react-reconciler/src/ReactFiberCommitViewTransitions.js
+15
-1
@@ -67,6 +67,20 @@ export function trackAppearingViewTransition(
67
appearingViewTransitions.set(name, state);
68
}
69
70
+export function trackEnterViewTransitions(placement: Fiber): void {
71
+ if (
72
+ placement.tag === ViewTransitionComponent ||
73
+ (placement.subtreeFlags & ViewTransitionStatic) !== NoFlags
74
+ ) {
75
+ // If an inserted or appearing Fiber is a ViewTransition component or has one as
76
+ // an immediate child, then that will trigger as an "Enter" in future passes.
77
+ // We don't do anything else for that case in the "before mutation" phase but we
78
+ // still have to mark it as needing to call startViewTransition if nothing else
79
+ // updates.
80
+ shouldStartViewTransition = true;
81
+ }
82
+}
83
+
84
// We can't cancel view transition children until we know that their parent also
85
// don't need to transition.
86
export let viewTransitionCancelableChildren: null | Array<
@@ -119,7 +133,6 @@ function applyViewTransitionToHostInstancesRecursive(
133
let inViewport = false;
134
while (child !== null) {
135
if (child.tag === HostComponent) {
122
- shouldStartViewTransition = true;
136
const instance: Instance = child.stateNode;
137
if (collectMeasurements !== null) {
138
const measurement = measureInstance(instance);
@@ -132,6 +145,7 @@ function applyViewTransitionToHostInstancesRecursive(
145
inViewport = true;
146
}
147
}
148
+ shouldStartViewTransition = true;
149
applyViewTransitionName(
150
instance,
151
viewTransitionHostInstanceIdx === 0
packages/react-reconciler/src/ReactFiberCommitWork.js
+7
@@ -235,6 +235,7 @@ import {
235
commitFragmentInstanceInsertionEffects,
236
} from './ReactFiberCommitHostEffects';
237
import {
238
+ trackEnterViewTransitions,
239
commitEnterViewTransitions,
240
commitExitViewTransitions,
241
commitBeforeUpdateViewTransition,
@@ -338,6 +339,9 @@ function commitBeforeMutationEffects_begin(isViewTransitionEligible: boolean) {
339
// to trigger updates of any nested view transitions and we shouldn't
340
// have any other before mutation effects since snapshot effects are
341
// only applied to updates. TODO: Model this using only flags.
342
+ if (isViewTransitionEligible) {
343
+ trackEnterViewTransitions(fiber);
344
+ }
345
commitBeforeMutationEffects_complete(isViewTransitionEligible);
346
continue;
347
}
@@ -367,6 +371,9 @@ function commitBeforeMutationEffects_begin(isViewTransitionEligible: boolean) {
371
// to trigger updates of any nested view transitions and we shouldn't
372
// have any other before mutation effects since snapshot effects are
373
// only applied to updates. TODO: Model this using only flags.
374
+ if (isViewTransitionEligible) {
375
+ trackEnterViewTransitions(fiber);
376
+ }
377
commitBeforeMutationEffects_complete(isViewTransitionEligible);
378
continue;
379
}