@samitouri / QOS-React-2 / commits / 4280563b04

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 }