Skip to content

Commit d91d28c

Browse files
authored
Use the JSX of the ViewTransition as the Stack Trace of "Animating" Traces (#34539)
Stacked on #34538. Track the Task of the first ViewTransition that we detected as animating. Use this as the Task as "Starting Animation", "Animating" etc. That way you can see which ViewTransition spawned the Animation. Although it's likely to be multiple. <img width="757" height="393" alt="Screenshot 2025-09-19 at 10 19 18 PM" src="https://github.com/user-attachments/assets/a6cdcb89-bd02-40ec-b3c3-11121c29e892" />
1 parent b4fe1e6 commit d91d28c

File tree

3 files changed

+41
-31
lines changed

3 files changed

+41
-31
lines changed

packages/react-reconciler/src/ReactFiberCommitViewTransitions.js

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ import {
3939
getViewTransitionName,
4040
getViewTransitionClassName,
4141
} from './ReactFiberViewTransitionComponent';
42+
import {trackAnimatingTask} from './ReactProfilerTimer';
43+
import {
44+
enableComponentPerformanceTrack,
45+
enableProfilerTimer,
46+
} from 'shared/ReactFeatureFlags';
4247

4348
export let shouldStartViewTransition: boolean = false;
4449

@@ -101,21 +106,27 @@ export function popViewTransitionCancelableScope(
101106

102107
let viewTransitionHostInstanceIdx = 0;
103108

104-
export function applyViewTransitionToHostInstances(
105-
child: null | Fiber,
109+
function applyViewTransitionToHostInstances(
110+
fiber: Fiber,
106111
name: string,
107112
className: ?string,
108113
collectMeasurements: null | Array<InstanceMeasurement>,
109114
stopAtNestedViewTransitions: boolean,
110115
): boolean {
111116
viewTransitionHostInstanceIdx = 0;
112-
return applyViewTransitionToHostInstancesRecursive(
113-
child,
117+
const inViewport = applyViewTransitionToHostInstancesRecursive(
118+
fiber.child,
114119
name,
115120
className,
116121
collectMeasurements,
117122
stopAtNestedViewTransitions,
118123
);
124+
if (enableProfilerTimer && enableComponentPerformanceTrack && inViewport) {
125+
if (fiber._debugTask != null) {
126+
trackAnimatingTask(fiber._debugTask);
127+
}
128+
}
129+
return inViewport;
119130
}
120131

121132
function applyViewTransitionToHostInstancesRecursive(
@@ -247,7 +258,7 @@ function commitAppearingPairViewTransitions(placement: Fiber): void {
247258
// We found a new appearing view transition with the same name as this deletion.
248259
// We'll transition between them.
249260
const inViewport = applyViewTransitionToHostInstances(
250-
child.child,
261+
child,
251262
name,
252263
className,
253264
null,
@@ -284,7 +295,7 @@ export function commitEnterViewTransitions(
284295
);
285296
if (className !== 'none') {
286297
const inViewport = applyViewTransitionToHostInstances(
287-
placement.child,
298+
placement,
288299
name,
289300
className,
290301
null,
@@ -355,7 +366,7 @@ function commitDeletedPairViewTransitions(deletion: Fiber): void {
355366
if (className !== 'none') {
356367
// We found a new appearing view transition with the same name as this deletion.
357368
const inViewport = applyViewTransitionToHostInstances(
358-
child.child,
369+
child,
359370
name,
360371
className,
361372
null,
@@ -406,7 +417,7 @@ export function commitExitViewTransitions(deletion: Fiber): void {
406417
);
407418
if (className !== 'none') {
408419
const inViewport = applyViewTransitionToHostInstances(
409-
deletion.child,
420+
deletion,
410421
name,
411422
className,
412423
null,
@@ -490,7 +501,7 @@ export function commitBeforeUpdateViewTransition(
490501
return;
491502
}
492503
applyViewTransitionToHostInstances(
493-
current.child,
504+
current,
494505
oldName,
495506
className,
496507
(current.memoizedState = []),
@@ -518,7 +529,7 @@ export function commitNestedViewTransitions(changedParent: Fiber): void {
518529
child.flags &= ~Update;
519530
if (className !== 'none') {
520531
applyViewTransitionToHostInstances(
521-
child.child,
532+
child,
522533
name,
523534
className,
524535
(child.memoizedState = []),

packages/react-reconciler/src/ReactFiberWorkLoop.js

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,7 @@ import {
324324
animatingLanes,
325325
retryClampTime,
326326
idleClampTime,
327+
animatingTask,
327328
} from './ReactProfilerTimer';
328329

329330
// DEV stuff
@@ -1995,7 +1996,7 @@ function prepareFreshStack(root: FiberRoot, lanes: Lanes): Fiber {
19951996
logAnimatingPhase(
19961997
blockingClampTime,
19971998
clampedRenderStartTime,
1998-
previousUpdateTask,
1999+
animatingTask,
19992000
);
20002001
}
20012002
logBlockingStart(
@@ -2048,7 +2049,7 @@ function prepareFreshStack(root: FiberRoot, lanes: Lanes): Fiber {
20482049
logAnimatingPhase(
20492050
transitionClampTime,
20502051
clampedRenderStartTime,
2051-
previousUpdateTask,
2052+
animatingTask,
20522053
);
20532054
}
20542055
logTransitionStart(
@@ -2069,14 +2070,14 @@ function prepareFreshStack(root: FiberRoot, lanes: Lanes): Fiber {
20692070
if (includesRetryLane(animatingLanes)) {
20702071
// If this lane is still animating, log the time from previous render finishing to now as animating.
20712072
setCurrentTrackFromLanes(SomeRetryLane);
2072-
logAnimatingPhase(retryClampTime, renderStartTime, previousUpdateTask);
2073+
logAnimatingPhase(retryClampTime, renderStartTime, animatingTask);
20732074
}
20742075
}
20752076
if (includesIdleGroupLanes(lanes)) {
20762077
if (includesIdleGroupLanes(animatingLanes)) {
20772078
// If this lane is still animating, log the time from previous render finishing to now as animating.
20782079
setCurrentTrackFromLanes(IdleLane);
2079-
logAnimatingPhase(idleClampTime, renderStartTime, previousUpdateTask);
2080+
logAnimatingPhase(idleClampTime, renderStartTime, animatingTask);
20802081
}
20812082
}
20822083
}
@@ -3667,12 +3668,7 @@ function commitRoot(
36673668
enableProfilerTimer ? suspendedViewTransition : (null: any),
36683669
enableProfilerTimer
36693670
? // This callback fires after "pendingEffects" so we need to snapshot the arguments.
3670-
finishedViewTransition.bind(
3671-
null,
3672-
lanes,
3673-
// TODO: Use a ViewTransition Task
3674-
__DEV__ ? workInProgressUpdateTask : null,
3675-
)
3671+
finishedViewTransition.bind(null, lanes)
36763672
: (null: any),
36773673
);
36783674
} else {
@@ -3712,15 +3708,13 @@ function suspendedViewTransition(reason: string): void {
37123708
}
37133709
}
37143710

3715-
function finishedViewTransition(
3716-
lanes: Lanes,
3717-
task: null | ConsoleTask, // DEV-only
3718-
): void {
3711+
function finishedViewTransition(lanes: Lanes): void {
37193712
if (enableProfilerTimer && enableComponentPerformanceTrack) {
37203713
if ((animatingLanes & lanes) === NoLanes) {
37213714
// Was already stopped by some other action or maybe other root.
37223715
return;
37233716
}
3717+
const task = animatingTask;
37243718
stopAnimating(lanes);
37253719
// If an affected track isn't in the middle of rendering or committing, log from the previous
37263720
// finished render until the end of the animation.
@@ -3835,7 +3829,7 @@ function flushLayoutEffects(): void {
38353829
commitEndTime, // The start is the end of the first commit part.
38363830
commitStartTime, // The end is the start of the second commit part.
38373831
suspendedViewTransitionReason,
3838-
workInProgressUpdateTask, // TODO: Use a ViewTransition Task and this is not safe to read in this phase.
3832+
animatingTask,
38393833
);
38403834
}
38413835
}
@@ -3938,7 +3932,7 @@ function flushSpawnedWork(): void {
39383932
startViewTransitionStartTime,
39393933
commitEndTime,
39403934
pendingDelayedCommitReason === ABORTED_VIEW_TRANSITION_COMMIT,
3941-
workInProgressUpdateTask, // TODO: Use a ViewTransition Task.
3935+
animatingTask,
39423936
);
39433937
if (pendingDelayedCommitReason !== ABORTED_VIEW_TRANSITION_COMMIT) {
39443938
pendingDelayedCommitReason = ANIMATION_STARTED_COMMIT;
@@ -4440,11 +4434,7 @@ function flushPassiveEffectsImpl() {
44404434
passiveEffectStartTime = now();
44414435
if (pendingDelayedCommitReason === ANIMATION_STARTED_COMMIT) {
44424436
// The animation was started, so we've been animating since that happened.
4443-
logAnimatingPhase(
4444-
commitEndTime,
4445-
passiveEffectStartTime,
4446-
workInProgressUpdateTask, // TODO: Use a ViewTransition Task
4447-
);
4437+
logAnimatingPhase(commitEndTime, passiveEffectStartTime, animatingTask);
44484438
} else {
44494439
logPaintYieldPhase(
44504440
commitEndTime,

packages/react-reconciler/src/ReactProfilerTimer.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ export let retryClampTime: number = -0;
9393
export let idleClampTime: number = -0;
9494

9595
export let animatingLanes: Lanes = NoLanes;
96+
export let animatingTask: null | ConsoleTask = null; // First ViewTransition applying an Animation.
9697

9798
export let yieldReason: SuspendedReason = (0: any);
9899
export let yieldStartTime: number = -1.1; // The time when we yielded to the event loop
@@ -601,8 +602,16 @@ export function transferActualDuration(fiber: Fiber): void {
601602

602603
export function startAnimating(lanes: Lanes): void {
603604
animatingLanes |= lanes;
605+
animatingTask = null;
604606
}
605607

606608
export function stopAnimating(lanes: Lanes): void {
607609
animatingLanes &= ~lanes;
610+
animatingTask = null;
611+
}
612+
613+
export function trackAnimatingTask(task: ConsoleTask): void {
614+
if (animatingTask === null) {
615+
animatingTask = task;
616+
}
608617
}

0 commit comments

Comments
 (0)