Skip to content

Commit 9403c3b

Browse files
author
Brian Vaughn
authored
Add Profiler callback when nested updates are scheduled (#20211)
This callback accepts the no parameters (except for the current interactions). Users of this hook can inspect the call stack to access and log the source location of the component.
1 parent 11a2ae3 commit 9403c3b

13 files changed

+617
-0
lines changed

packages/react-reconciler/src/ReactFiberWorkLoop.new.js

+38
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {
2222
replayFailedUnitOfWorkWithInvokeGuardedCallback,
2323
enableProfilerTimer,
2424
enableProfilerNestedUpdatePhase,
25+
enableProfilerNestedUpdateScheduledHook,
2526
enableSchedulerTracing,
2627
warnAboutUnmockedScheduler,
2728
deferRenderPhaseUpdateToNextBatch,
@@ -110,6 +111,7 @@ import {
110111
ForwardRef,
111112
MemoComponent,
112113
SimpleMemoComponent,
114+
Profiler,
113115
} from './ReactWorkTags';
114116
import {LegacyRoot} from './ReactRootTags';
115117
import {
@@ -258,6 +260,10 @@ let workInProgress: Fiber | null = null;
258260
// The lanes we're rendering
259261
let workInProgressRootRenderLanes: Lanes = NoLanes;
260262

263+
// Only used when enableProfilerNestedUpdateScheduledHook is true;
264+
// to track which root is currently committing layout effects.
265+
let rootCommittingMutationOrLayoutEffects: FiberRoot | null = null;
266+
261267
// Stack that allows components to change the render lanes for its subtree
262268
// This is a superset of the lanes we started working on at the root. The only
263269
// case where it's different from `workInProgressRootRenderLanes` is when we
@@ -509,6 +515,30 @@ export function scheduleUpdateOnFiber(
509515
// Mark that the root has a pending update.
510516
markRootUpdated(root, lane, eventTime);
511517

518+
if (enableProfilerTimer && enableProfilerNestedUpdateScheduledHook) {
519+
if (
520+
executionContext === CommitContext &&
521+
root === rootCommittingMutationOrLayoutEffects
522+
) {
523+
if (fiber.mode & ProfileMode) {
524+
let current = fiber;
525+
while (current !== null) {
526+
if (current.tag === Profiler) {
527+
const {onNestedUpdateScheduled} = current.memoizedProps;
528+
if (typeof onNestedUpdateScheduled === 'function') {
529+
if (enableSchedulerTracing) {
530+
onNestedUpdateScheduled(root.memoizedInteractions);
531+
} else {
532+
onNestedUpdateScheduled();
533+
}
534+
}
535+
}
536+
current = current.return;
537+
}
538+
}
539+
}
540+
}
541+
512542
if (root === workInProgressRoot) {
513543
// Received an update to a tree that's in the middle of rendering. Mark
514544
// that there was an interleaved update work on this root. Unless the
@@ -1898,6 +1928,10 @@ function commitRootImpl(root, renderPriorityLevel) {
18981928
recordCommitTime();
18991929
}
19001930

1931+
if (enableProfilerTimer && enableProfilerNestedUpdateScheduledHook) {
1932+
rootCommittingMutationOrLayoutEffects = root;
1933+
}
1934+
19011935
// The next phase is the mutation phase, where we mutate the host tree.
19021936
commitMutationEffects(finishedWork, root, renderPriorityLevel);
19031937

@@ -1936,6 +1970,10 @@ function commitRootImpl(root, renderPriorityLevel) {
19361970
markLayoutEffectsStopped();
19371971
}
19381972

1973+
if (enableProfilerTimer && enableProfilerNestedUpdateScheduledHook) {
1974+
rootCommittingMutationOrLayoutEffects = null;
1975+
}
1976+
19391977
// Tell Scheduler to yield at the end of the frame, so the browser has an
19401978
// opportunity to paint.
19411979
requestPaint();

packages/react-reconciler/src/ReactFiberWorkLoop.old.js

+40
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
enableProfilerTimer,
2424
enableProfilerCommitHooks,
2525
enableProfilerNestedUpdatePhase,
26+
enableProfilerNestedUpdateScheduledHook,
2627
enableSchedulerTracing,
2728
warnAboutUnmockedScheduler,
2829
deferRenderPhaseUpdateToNextBatch,
@@ -112,6 +113,7 @@ import {
112113
OffscreenComponent,
113114
LegacyHiddenComponent,
114115
ScopeComponent,
116+
Profiler,
115117
} from './ReactWorkTags';
116118
import {LegacyRoot} from './ReactRootTags';
117119
import {
@@ -329,6 +331,10 @@ let hasUncaughtError = false;
329331
let firstUncaughtError = null;
330332
let legacyErrorBoundariesThatAlreadyFailed: Set<mixed> | null = null;
331333

334+
// Only used when enableProfilerNestedUpdateScheduledHook is true;
335+
// to track which root is currently committing layout effects.
336+
let rootCommittingMutationOrLayoutEffects: FiberRoot | null = null;
337+
332338
let rootDoesHavePassiveEffects: boolean = false;
333339
let rootWithPendingPassiveEffects: FiberRoot | null = null;
334340
let pendingPassiveEffectsRenderPriority: ReactPriorityLevel = NoSchedulerPriority;
@@ -533,6 +539,30 @@ export function scheduleUpdateOnFiber(
533539
// Mark that the root has a pending update.
534540
markRootUpdated(root, lane, eventTime);
535541

542+
if (enableProfilerTimer && enableProfilerNestedUpdateScheduledHook) {
543+
if (
544+
executionContext === CommitContext &&
545+
root === rootCommittingMutationOrLayoutEffects
546+
) {
547+
if (fiber.mode & ProfileMode) {
548+
let current = fiber;
549+
while (current !== null) {
550+
if (current.tag === Profiler) {
551+
const {onNestedUpdateScheduled} = current.memoizedProps;
552+
if (typeof onNestedUpdateScheduled === 'function') {
553+
if (enableSchedulerTracing) {
554+
onNestedUpdateScheduled(root.memoizedInteractions);
555+
} else {
556+
onNestedUpdateScheduled();
557+
}
558+
}
559+
}
560+
current = current.return;
561+
}
562+
}
563+
}
564+
}
565+
536566
if (root === workInProgressRoot) {
537567
// Received an update to a tree that's in the middle of rendering. Mark
538568
// that there was an interleaved update work on this root. Unless the
@@ -2047,6 +2077,12 @@ function commitRootImpl(root, renderPriorityLevel) {
20472077
recordCommitTime();
20482078
}
20492079

2080+
if (enableProfilerTimer && enableProfilerNestedUpdateScheduledHook) {
2081+
// Track the root here, rather than in commitLayoutEffects(), because of ref setters.
2082+
// Updates scheduled during ref detachment should also be flagged.
2083+
rootCommittingMutationOrLayoutEffects = root;
2084+
}
2085+
20502086
// The next phase is the mutation phase, where we mutate the host tree.
20512087
nextEffect = firstEffect;
20522088
do {
@@ -2112,6 +2148,10 @@ function commitRootImpl(root, renderPriorityLevel) {
21122148

21132149
nextEffect = null;
21142150

2151+
if (enableProfilerTimer && enableProfilerNestedUpdateScheduledHook) {
2152+
rootCommittingMutationOrLayoutEffects = null;
2153+
}
2154+
21152155
// Tell Scheduler to yield at the end of the frame, so the browser has an
21162156
// opportunity to paint.
21172157
requestPaint();

0 commit comments

Comments
 (0)