Run Component Track Logs in the console.createTask() of the Fiber (#32809)
Stacked on #32736. That way you can find the owner stack of each component that rerendered for context. In addition to the JSX callsite tasks that we already track, I also added tracking of the first `setState` call before rendering. We then run the "Update" entries in that task. That way you can find the callsite of the first setState and therefore the "cause" of a render starting by selecting the "Update" track. Unfortunately this is blocked on bugs in Chrome that makes it so that these stacks are not reliable in the Performance tab. It basically just doesn't work.
Sebastian Markbåge committed
Apr 29, 2025 at 22:17 UTC
62960c67c84da7e85d7515ef8447cea0ba0824a6
7 files changed
+358
-113
packages/react-client/src/ReactFlightPerformanceTrack.js
+48
-16
@@ -78,14 +78,30 @@ export function logComponentRender(
78
: 'error';
79
const entryName =
80
isPrimaryEnv || env === undefined ? name : name + ' [' + env + ']';
81
- console.timeStamp(
82
- entryName,
83
- startTime < 0 ? 0 : startTime,
84
- childrenEndTime,
85
- trackNames[trackIdx],
86
- COMPONENTS_TRACK,
87
- color,
88
- );
81
+ const debugTask = componentInfo.debugTask;
82
+ if (__DEV__ && debugTask) {
83
+ debugTask.run(
84
+ // $FlowFixMe[method-unbinding]
85
+ console.timeStamp.bind(
86
+ console,
87
+ entryName,
88
+ startTime < 0 ? 0 : startTime,
89
+ childrenEndTime,
90
+ trackNames[trackIdx],
91
+ COMPONENTS_TRACK,
92
+ color,
93
+ ),
94
+ );
95
+ } else {
96
+ console.timeStamp(
97
+ entryName,
98
+ startTime < 0 ? 0 : startTime,
99
+ childrenEndTime,
100
+ trackNames[trackIdx],
101
+ COMPONENTS_TRACK,
102
+ color,
103
+ );
104
+ }
105
}
106
}
107
@@ -154,13 +170,29 @@ export function logDedupedComponentRender(
170
if (supportsUserTiming && endTime >= 0 && trackIdx < 10) {
171
const name = componentInfo.name;
172
const entryName = name + ' [deduped]';
157
- console.timeStamp(
158
- entryName,
159
- startTime < 0 ? 0 : startTime,
160
- endTime,
161
- trackNames[trackIdx],
162
- COMPONENTS_TRACK,
163
- 'tertiary-light',
164
- );
173
+ const debugTask = componentInfo.debugTask;
174
+ if (__DEV__ && debugTask) {
175
+ debugTask.run(
176
+ // $FlowFixMe[method-unbinding]
177
+ console.timeStamp.bind(
178
+ console,
179
+ entryName,
180
+ startTime < 0 ? 0 : startTime,
181
+ endTime,
182
+ trackNames[trackIdx],
183
+ COMPONENTS_TRACK,
184
+ 'tertiary-light',
185
+ ),
186
+ );
187
+ } else {
188
+ console.timeStamp(
189
+ entryName,
190
+ startTime < 0 ? 0 : startTime,
191
+ endTime,
192
+ trackNames[trackIdx],
193
+ COMPONENTS_TRACK,
194
+ 'tertiary-light',
195
+ );
196
+ }
197
}
198
}
packages/react-reconciler/src/ReactFiberClassComponent.js
+3
-3
@@ -180,7 +180,7 @@ const classComponentUpdater = {
180
181
const root = enqueueUpdate(fiber, update, lane);
182
if (root !== null) {
183
- startUpdateTimerByLane(lane);
183
+ startUpdateTimerByLane(lane, 'this.setState()');
184
scheduleUpdateOnFiber(root, fiber, lane);
185
entangleTransitions(root, fiber, lane);
186
}
@@ -206,7 +206,7 @@ const classComponentUpdater = {
206
207
const root = enqueueUpdate(fiber, update, lane);
208
if (root !== null) {
209
- startUpdateTimerByLane(lane);
209
+ startUpdateTimerByLane(lane, 'this.replaceState()');
210
scheduleUpdateOnFiber(root, fiber, lane);
211
entangleTransitions(root, fiber, lane);
212
}
@@ -232,7 +232,7 @@ const classComponentUpdater = {
232
233
const root = enqueueUpdate(fiber, update, lane);
234
if (root !== null) {
235
- startUpdateTimerByLane(lane);
235
+ startUpdateTimerByLane(lane, 'this.forceUpdate()');
236
scheduleUpdateOnFiber(root, fiber, lane);
237
entangleTransitions(root, fiber, lane);
238
}
packages/react-reconciler/src/ReactFiberHooks.js
+7
-4
@@ -1847,6 +1847,8 @@ function updateStoreInstance<T>(
1847
// snapsho and getSnapshot values to bail out. We need to check one more time.
1848
if (checkIfSnapshotChanged(inst)) {
1849
// Force a re-render.
1850
+ // We intentionally don't log update times and stacks here because this
1851
+ // was not an external trigger but rather an internal one.
1852
forceStoreRerender(fiber);
1853
}
1854
}
@@ -1861,6 +1863,7 @@ function subscribeToStore<T>(
1863
// read from the store.
1864
if (checkIfSnapshotChanged(inst)) {
1865
// Force a re-render.
1866
+ startUpdateTimerByLane(SyncLane, 'updateSyncExternalStore()');
1867
forceStoreRerender(fiber);
1868
}
1869
};
@@ -3503,7 +3506,7 @@ function refreshCache<T>(fiber: Fiber, seedKey: ?() => T, seedValue: T): void {
3506
const refreshUpdate = createLegacyQueueUpdate(lane);
3507
const root = enqueueLegacyQueueUpdate(provider, refreshUpdate, lane);
3508
if (root !== null) {
3506
- startUpdateTimerByLane(lane);
3509
+ startUpdateTimerByLane(lane, 'refresh()');
3510
scheduleUpdateOnFiber(root, provider, lane);
3511
entangleLegacyQueueTransitions(root, provider, lane);
3512
}
@@ -3572,7 +3575,7 @@ function dispatchReducerAction<S, A>(
3575
} else {
3576
const root = enqueueConcurrentHookUpdate(fiber, queue, update, lane);
3577
if (root !== null) {
3575
- startUpdateTimerByLane(lane);
3578
+ startUpdateTimerByLane(lane, 'dispatch()');
3579
scheduleUpdateOnFiber(root, fiber, lane);
3580
entangleTransitionUpdate(root, queue, lane);
3581
}
@@ -3606,7 +3609,7 @@ function dispatchSetState<S, A>(
3609
lane,
3610
);
3611
if (didScheduleUpdate) {
3609
- startUpdateTimerByLane(lane);
3612
+ startUpdateTimerByLane(lane, 'setState()');
3613
}
3614
markUpdateInDevTools(fiber, lane, action);
3615
}
@@ -3768,7 +3771,7 @@ function dispatchOptimisticSetState<S, A>(
3771
// will never be attempted before the optimistic update. This currently
3772
// holds because the optimistic update is always synchronous. If we ever
3773
// change that, we'll need to account for this.
3771
- startUpdateTimerByLane(lane);
3774
+ startUpdateTimerByLane(lane, 'setOptimistic()');
3775
scheduleUpdateOnFiber(root, fiber, lane);
3776
// Optimistic updates are always synchronous, so we don't need to call
3777
// entangleTransitionUpdate here.
packages/react-reconciler/src/ReactFiberPerformanceTrack.js
+280
-88
@@ -103,14 +103,29 @@ function logComponentTrigger(
103
trigger: string,
104
) {
105
if (supportsUserTiming) {
106
- console.timeStamp(
107
- trigger,
108
- startTime,
109
- endTime,
110
- COMPONENTS_TRACK,
111
- undefined,
112
- 'warning',
113
- );
106
+ const debugTask = fiber._debugTask;
107
+ if (__DEV__ && debugTask) {
108
+ debugTask.run(
109
+ console.timeStamp.bind(
110
+ console,
111
+ trigger,
112
+ startTime,
113
+ endTime,
114
+ COMPONENTS_TRACK,
115
+ undefined,
116
+ 'warning',
117
+ ),
118
+ );
119
+ } else {
120
+ console.timeStamp(
121
+ trigger,
122
+ startTime,
123
+ endTime,
124
+ COMPONENTS_TRACK,
125
+ undefined,
126
+ 'warning',
127
+ );
128
+ }
129
}
130
}
131
@@ -178,14 +193,30 @@ export function logComponentRender(
193
? 'tertiary-dark'
194
: 'primary-dark'
195
: 'error';
181
- console.timeStamp(
182
- name,
183
- startTime,
184
- endTime,
185
- COMPONENTS_TRACK,
186
- undefined,
187
- color,
188
- );
196
+ const debugTask = fiber._debugTask;
197
+ if (__DEV__ && debugTask) {
198
+ debugTask.run(
199
+ // $FlowFixMe[method-unbinding]
200
+ console.timeStamp.bind(
201
+ console,
202
+ name,
203
+ startTime,
204
+ endTime,
205
+ COMPONENTS_TRACK,
206
+ undefined,
207
+ color,
208
+ ),
209
+ );
210
+ } else {
211
+ console.timeStamp(
212
+ name,
213
+ startTime,
214
+ endTime,
215
+ COMPONENTS_TRACK,
216
+ undefined,
217
+ color,
218
+ );
219
+ }
220
}
221
}
222
@@ -207,9 +238,18 @@ export function logComponentErrored(
238
// $FlowFixMe[method-unbinding]
239
typeof performance.measure === 'function'
240
) {
241
+ let debugTask: ?ConsoleTask = null;
242
const properties = [];
243
for (let i = 0; i < errors.length; i++) {
244
const capturedValue = errors[i];
245
+ if (debugTask == null && capturedValue.source !== null) {
246
+ // If the captured value has a source Fiber, use its debugTask for
247
+ // the stack instead of the error boundary's stack. So you can find
248
+ // which component errored since we don't show the errored render tree.
249
+ // TODO: Ideally we should instead, store the failed fibers and log the
250
+ // whole subtree including the component that errored.
251
+ debugTask = capturedValue.source._debugTask;
252
+ }
253
const error = capturedValue.value;
254
const message =
255
typeof error === 'object' &&
@@ -221,7 +261,12 @@ export function logComponentErrored(
261
String(error);
262
properties.push(['Error', message]);
263
}
224
- performance.measure(name, {
264
+ if (debugTask == null) {
265
+ // If the captured values don't have a debug task, fallback to the
266
+ // error boundary itself.
267
+ debugTask = fiber._debugTask;
268
+ }
269
+ const options = {
270
start: startTime,
271
end: endTime,
272
detail: {
@@ -235,7 +280,15 @@ export function logComponentErrored(
280
properties,
281
},
282
},
238
- });
283
+ };
284
+ if (__DEV__ && debugTask) {
285
+ debugTask.run(
286
+ // $FlowFixMe[method-unbinding]
287
+ performance.measure.bind(performance, name, options),
288
+ );
289
+ } else {
290
+ performance.measure(name, options);
291
+ }
292
} else {
293
console.timeStamp(
294
name,
@@ -281,7 +334,7 @@ function logComponentEffectErrored(
334
String(error);
335
properties.push(['Error', message]);
336
}
284
- performance.measure(name, {
337
+ const options = {
338
start: startTime,
339
end: endTime,
340
detail: {
@@ -292,7 +345,16 @@ function logComponentEffectErrored(
345
properties,
346
},
347
},
295
- });
348
+ };
349
+ const debugTask = fiber._debugTask;
350
+ if (debugTask) {
351
+ debugTask.run(
352
+ // $FlowFixMe[method-unbinding]
353
+ performance.measure.bind(performance, name, options),
354
+ );
355
+ } else {
356
+ performance.measure(name, options);
357
+ }
358
} else {
359
console.timeStamp(
360
name,
@@ -331,14 +393,30 @@ export function logComponentEffect(
393
: selfTime < 500
394
? 'secondary-dark'
395
: 'error';
334
- console.timeStamp(
335
- name,
336
- startTime,
337
- endTime,
338
- COMPONENTS_TRACK,
339
- undefined,
340
- color,
341
- );
396
+ const debugTask = fiber._debugTask;
397
+ if (__DEV__ && debugTask) {
398
+ debugTask.run(
399
+ // $FlowFixMe[method-unbinding]
400
+ console.timeStamp.bind(
401
+ console,
402
+ name,
403
+ startTime,
404
+ endTime,
405
+ COMPONENTS_TRACK,
406
+ undefined,
407
+ color,
408
+ ),
409
+ );
410
+ } else {
411
+ console.timeStamp(
412
+ name,
413
+ startTime,
414
+ endTime,
415
+ COMPONENTS_TRACK,
416
+ undefined,
417
+ color,
418
+ );
419
+ }
420
}
421
}
422
@@ -379,14 +457,30 @@ export function logSuspendedYieldTime(
457
suspendedFiber: Fiber,
458
): void {
459
if (supportsUserTiming) {
382
- console.timeStamp(
383
- 'Suspended',
384
- startTime,
385
- endTime,
386
- COMPONENTS_TRACK,
387
- undefined,
388
- 'primary-light',
389
- );
460
+ const debugTask = suspendedFiber._debugTask;
461
+ if (__DEV__ && debugTask) {
462
+ debugTask.run(
463
+ // $FlowFixMe[method-unbinding]
464
+ console.timeStamp.bind(
465
+ console,
466
+ 'Suspended',
467
+ startTime,
468
+ endTime,
469
+ COMPONENTS_TRACK,
470
+ undefined,
471
+ 'primary-light',
472
+ ),
473
+ );
474
+ } else {
475
+ console.timeStamp(
476
+ 'Suspended',
477
+ startTime,
478
+ endTime,
479
+ COMPONENTS_TRACK,
480
+ undefined,
481
+ 'primary-light',
482
+ );
483
+ }
484
}
485
}
486
@@ -396,14 +490,30 @@ export function logActionYieldTime(
490
suspendedFiber: Fiber,
491
): void {
492
if (supportsUserTiming) {
399
- console.timeStamp(
400
- 'Action',
401
- startTime,
402
- endTime,
403
- COMPONENTS_TRACK,
404
- undefined,
405
- 'primary-light',
406
- );
493
+ const debugTask = suspendedFiber._debugTask;
494
+ if (__DEV__ && debugTask) {
495
+ debugTask.run(
496
+ // $FlowFixMe[method-unbinding]
497
+ console.timeStamp.bind(
498
+ console,
499
+ 'Action',
500
+ startTime,
501
+ endTime,
502
+ COMPONENTS_TRACK,
503
+ undefined,
504
+ 'primary-light',
505
+ ),
506
+ );
507
+ } else {
508
+ console.timeStamp(
509
+ 'Action',
510
+ startTime,
511
+ endTime,
512
+ COMPONENTS_TRACK,
513
+ undefined,
514
+ 'primary-light',
515
+ );
516
+ }
517
}
518
}
519
@@ -415,6 +525,7 @@ export function logBlockingStart(
525
isSpawnedUpdate: boolean,
526
renderStartTime: number,
527
lanes: Lanes,
528
+ debugTask: null | ConsoleTask, // DEV-only
529
): void {
530
if (supportsUserTiming) {
531
currentTrack = 'Blocking';
@@ -424,14 +535,29 @@ export function logBlockingStart(
535
if (eventTime > 0 && eventType !== null) {
536
// Log the time from the event timeStamp until we called setState.
537
const color = eventIsRepeat ? 'secondary-light' : 'warning';
427
- console.timeStamp(
428
- eventIsRepeat ? '' : 'Event: ' + eventType,
429
- eventTime,
430
- updateTime > 0 ? updateTime : renderStartTime,
431
- currentTrack,
432
- LANES_TRACK_GROUP,
433
- color,
434
- );
538
+ if (__DEV__ && debugTask) {
539
+ debugTask.run(
540
+ // $FlowFixMe[method-unbinding]
541
+ console.timeStamp.bind(
542
+ console,
543
+ eventIsRepeat ? '' : 'Event: ' + eventType,
544
+ eventTime,
545
+ updateTime > 0 ? updateTime : renderStartTime,
546
+ currentTrack,
547
+ LANES_TRACK_GROUP,
548
+ color,
549
+ ),
550
+ );
551
+ } else {
552
+ console.timeStamp(
553
+ eventIsRepeat ? '' : 'Event: ' + eventType,
554
+ eventTime,
555
+ updateTime > 0 ? updateTime : renderStartTime,
556
+ currentTrack,
557
+ LANES_TRACK_GROUP,
558
+ color,
559
+ );
560
+ }
561
}
562
if (updateTime > 0) {
563
// Log the time from when we called setState until we started rendering.
@@ -440,18 +566,37 @@ export function logBlockingStart(
566
: includesOnlyHydrationOrOffscreenLanes(lanes)
567
? 'tertiary-light'
568
: 'primary-light';
443
- console.timeStamp(
444
- isSpawnedUpdate
445
- ? 'Cascading Update'
446
- : renderStartTime - updateTime > 5
447
- ? 'Update Blocked'
448
- : 'Update',
449
- updateTime,
450
- renderStartTime,
451
- currentTrack,
452
- LANES_TRACK_GROUP,
453
- color,
454
- );
569
+ if (__DEV__ && debugTask) {
570
+ debugTask.run(
571
+ // $FlowFixMe[method-unbinding]
572
+ console.timeStamp.bind(
573
+ console,
574
+ isSpawnedUpdate
575
+ ? 'Cascading Update'
576
+ : renderStartTime - updateTime > 5
577
+ ? 'Update Blocked'
578
+ : 'Update',
579
+ updateTime,
580
+ renderStartTime,
581
+ currentTrack,
582
+ LANES_TRACK_GROUP,
583
+ color,
584
+ ),
585
+ );
586
+ } else {
587
+ console.timeStamp(
588
+ isSpawnedUpdate
589
+ ? 'Cascading Update'
590
+ : renderStartTime - updateTime > 5
591
+ ? 'Update Blocked'
592
+ : 'Update',
593
+ updateTime,
594
+ renderStartTime,
595
+ currentTrack,
596
+ LANES_TRACK_GROUP,
597
+ color,
598
+ );
599
+ }
600
}
601
}
602
}
@@ -463,6 +608,7 @@ export function logTransitionStart(
608
eventType: null | string,
609
eventIsRepeat: boolean,
610
renderStartTime: number,
611
+ debugTask: null | ConsoleTask, // DEV-only
612
): void {
613
if (supportsUserTiming) {
614
currentTrack = 'Transition';
@@ -475,36 +621,82 @@ export function logTransitionStart(
621
: updateTime > 0
622
? updateTime
623
: renderStartTime;
478
- console.timeStamp(
479
- eventIsRepeat ? '' : 'Event: ' + eventType,
480
- eventTime,
481
- endTime,
482
- currentTrack,
483
- LANES_TRACK_GROUP,
484
- color,
485
- );
624
+ if (__DEV__ && debugTask) {
625
+ debugTask.run(
626
+ // $FlowFixMe[method-unbinding]
627
+ console.timeStamp.bind(
628
+ console,
629
+ eventIsRepeat ? '' : 'Event: ' + eventType,
630
+ eventTime,
631
+ endTime,
632
+ currentTrack,
633
+ LANES_TRACK_GROUP,
634
+ color,
635
+ ),
636
+ );
637
+ } else {
638
+ console.timeStamp(
639
+ eventIsRepeat ? '' : 'Event: ' + eventType,
640
+ eventTime,
641
+ endTime,
642
+ currentTrack,
643
+ LANES_TRACK_GROUP,
644
+ color,
645
+ );
646
+ }
647
}
648
if (startTime > 0) {
649
// Log the time from when we started an async transition until we called setState or started rendering.
489
- console.timeStamp(
490
- 'Action',
491
- startTime,
492
- updateTime > 0 ? updateTime : renderStartTime,
493
- currentTrack,
494
- LANES_TRACK_GROUP,
495
- 'primary-dark',
496
- );
650
+ // TODO: Ideally this would use the debugTask of the startTransition call perhaps.
651
+ if (__DEV__ && debugTask) {
652
+ debugTask.run(
653
+ // $FlowFixMe[method-unbinding]
654
+ console.timeStamp.bind(
655
+ console,
656
+ 'Action',
657
+ startTime,
658
+ updateTime > 0 ? updateTime : renderStartTime,
659
+ currentTrack,
660
+ LANES_TRACK_GROUP,
661
+ 'primary-dark',
662
+ ),
663
+ );
664
+ } else {
665
+ console.timeStamp(
666
+ 'Action',
667
+ startTime,
668
+ updateTime > 0 ? updateTime : renderStartTime,
669
+ currentTrack,
670
+ LANES_TRACK_GROUP,
671
+ 'primary-dark',
672
+ );
673
+ }
674
}
675
if (updateTime > 0) {
676
// Log the time from when we called setState until we started rendering.
500
- console.timeStamp(
501
- renderStartTime - updateTime > 5 ? 'Update Blocked' : 'Update',
502
- updateTime,
503
- renderStartTime,
504
- currentTrack,
505
- LANES_TRACK_GROUP,
506
- 'primary-light',
507
- );
677
+ if (__DEV__ && debugTask) {
678
+ debugTask.run(
679
+ // $FlowFixMe[method-unbinding]
680
+ console.timeStamp.bind(
681
+ console,
682
+ renderStartTime - updateTime > 5 ? 'Update Blocked' : 'Update',
683
+ updateTime,
684
+ renderStartTime,
685
+ currentTrack,
686
+ LANES_TRACK_GROUP,
687
+ 'primary-light',
688
+ ),
689
+ );
690
+ } else {
691
+ console.timeStamp(
692
+ renderStartTime - updateTime > 5 ? 'Update Blocked' : 'Update',
693
+ updateTime,
694
+ renderStartTime,
695
+ currentTrack,
696
+ LANES_TRACK_GROUP,
697
+ 'primary-light',
698
+ );
699
+ }
700
}
701
}
702
}
packages/react-reconciler/src/ReactFiberReconciler.js
+1
-1
@@ -443,7 +443,7 @@ function updateContainerImpl(
443
444
const root = enqueueUpdate(rootFiber, update, lane);
445
if (root !== null) {
446
- startUpdateTimerByLane(lane);
446
+ startUpdateTimerByLane(lane, 'root.render()');
447
scheduleUpdateOnFiber(root, rootFiber, lane);
448
entangleTransitions(root, rootFiber, lane);
449
}
packages/react-reconciler/src/ReactFiberWorkLoop.js
+4
@@ -264,6 +264,7 @@ import {
264
import {
265
blockingClampTime,
266
blockingUpdateTime,
267
+ blockingUpdateTask,
268
blockingEventTime,
269
blockingEventType,
270
blockingEventIsRepeat,
@@ -272,6 +273,7 @@ import {
273
transitionClampTime,
274
transitionStartTime,
275
transitionUpdateTime,
276
+ transitionUpdateTask,
277
transitionEventTime,
278
transitionEventType,
279
transitionEventIsRepeat,
@@ -1914,6 +1916,7 @@ function prepareFreshStack(root: FiberRoot, lanes: Lanes): Fiber {
1916
blockingSpawnedUpdate,
1917
renderStartTime,
1918
lanes,
1919
+ blockingUpdateTask,
1920
);
1921
clearBlockingTimers();
1922
}
@@ -1950,6 +1953,7 @@ function prepareFreshStack(root: FiberRoot, lanes: Lanes): Fiber {
1953
transitionEventType,
1954
transitionEventIsRepeat,
1955
renderStartTime,
1956
+ transitionUpdateTask,
1957
);
1958
clearTransitionTimers();
1959
}
packages/react-reconciler/src/ReactProfilerTimer.js
+15
-1
@@ -41,6 +41,13 @@ import * as Scheduler from 'scheduler';
41
42
const {unstable_now: now} = Scheduler;
43
44
+const createTask =
45
+ // eslint-disable-next-line react-internal/no-production-logging
46
+ __DEV__ && console.createTask
47
+ ? // eslint-disable-next-line react-internal/no-production-logging
48
+ console.createTask
49
+ : (name: string) => null;
50
+
51
export let renderStartTime: number = -0;
52
export let commitStartTime: number = -0;
53
export let commitEndTime: number = -0;
@@ -54,6 +61,7 @@ export let componentEffectErrors: null | Array<CapturedValue<mixed>> = null;
61
62
export let blockingClampTime: number = -0;
63
export let blockingUpdateTime: number = -1.1; // First sync setState scheduled.
64
+export let blockingUpdateTask: null | ConsoleTask = null; // First sync setState's stack trace.
65
export let blockingEventTime: number = -1.1; // Event timeStamp of the first setState.
66
export let blockingEventType: null | string = null; // Event type of the first setState.
67
export let blockingEventIsRepeat: boolean = false;
@@ -63,6 +71,7 @@ export let blockingSuspendedTime: number = -1.1;
71
export let transitionClampTime: number = -0;
72
export let transitionStartTime: number = -1.1; // First startTransition call before setState.
73
export let transitionUpdateTime: number = -1.1; // First transition setState scheduled.
74
+export let transitionUpdateTask: null | ConsoleTask = null; // First transition setState's stack trace.
75
export let transitionEventTime: number = -1.1; // Event timeStamp of the first transition.
76
export let transitionEventType: null | string = null; // Event type of the first transition.
77
export let transitionEventIsRepeat: boolean = false;
@@ -79,13 +88,14 @@ export function startYieldTimer(reason: SuspendedReason) {
88
yieldReason = reason;
89
}
90
82
-export function startUpdateTimerByLane(lane: Lane): void {
91
+export function startUpdateTimerByLane(lane: Lane, method: string): void {
92
if (!enableProfilerTimer || !enableComponentPerformanceTrack) {
93
return;
94
}
95
if (isSyncLane(lane) || isBlockingLane(lane)) {
96
if (blockingUpdateTime < 0) {
97
blockingUpdateTime = now();
98
+ blockingUpdateTask = createTask(method);
99
if (isAlreadyRendering()) {
100
blockingSpawnedUpdate = true;
101
}
@@ -108,6 +118,7 @@ export function startUpdateTimerByLane(lane: Lane): void {
118
} else if (isTransitionLane(lane)) {
119
if (transitionUpdateTime < 0) {
120
transitionUpdateTime = now();
121
+ transitionUpdateTask = createTask(method);
122
if (transitionStartTime < 0) {
123
const newEventTime = resolveEventTimeStamp();
124
const newEventType = resolveEventType();
@@ -155,6 +166,7 @@ export function trackSuspendedTime(lanes: Lanes, renderEndTime: number) {
166
167
export function clearBlockingTimers(): void {
168
blockingUpdateTime = -1.1;
169
+ blockingUpdateTask = null;
170
blockingSuspendedTime = -1.1;
171
blockingEventIsRepeat = true;
172
blockingSpawnedUpdate = false;
@@ -194,6 +206,7 @@ export function startActionStateUpdate(): void {
206
}
207
if (transitionUpdateTime < 0) {
208
transitionUpdateTime = ACTION_STATE_MARKER;
209
+ transitionUpdateTask = null;
210
}
211
}
212
@@ -204,6 +217,7 @@ export function clearAsyncTransitionTimer(): void {
217
export function clearTransitionTimers(): void {
218
transitionStartTime = -1.1;
219
transitionUpdateTime = -1.1;
220
+ transitionUpdateTask = null;
221
transitionSuspendedTime = -1.1;
222
transitionEventIsRepeat = true;
223
}