Skip to content

Commit 9403c3b

Browse files
author
Brian Vaughn
authoredNov 12, 2020
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();

‎packages/react/src/__tests__/ReactProfiler-test.internal.js

+524
Large diffs are not rendered by default.

‎packages/shared/ReactFeatureFlags.js

+4
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ export const enableProfilerCommitHooks = false;
3939
// Phase param passed to onRender callback differentiates between an "update" and a "cascading-update".
4040
export const enableProfilerNestedUpdatePhase = false;
4141

42+
// Profiler API accepts a function to be called when a nested update is scheduled.
43+
// This callback accepts the component type (class instance or function) the update is scheduled for.
44+
export const enableProfilerNestedUpdateScheduledHook = false;
45+
4246
// Trace which interactions trigger each commit.
4347
export const enableSchedulerTracing = __PROFILE__;
4448

‎packages/shared/forks/ReactFeatureFlags.native-fb.js

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export const enableSchedulingProfiler = false;
1616
export const enableProfilerTimer = __PROFILE__;
1717
export const enableProfilerCommitHooks = false;
1818
export const enableProfilerNestedUpdatePhase = false;
19+
export const enableProfilerNestedUpdateScheduledHook = false;
1920
export const enableSchedulerTracing = __PROFILE__;
2021
export const enableSuspenseServerRenderer = false;
2122
export const enableSelectiveHydration = false;

‎packages/shared/forks/ReactFeatureFlags.native-oss.js

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export const warnAboutDeprecatedLifecycles = true;
1818
export const enableProfilerTimer = __PROFILE__;
1919
export const enableProfilerCommitHooks = false;
2020
export const enableProfilerNestedUpdatePhase = false;
21+
export const enableProfilerNestedUpdateScheduledHook = false;
2122
export const enableSchedulerTracing = __PROFILE__;
2223
export const enableSuspenseServerRenderer = false;
2324
export const enableSelectiveHydration = false;

‎packages/shared/forks/ReactFeatureFlags.test-renderer.js

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export const replayFailedUnitOfWorkWithInvokeGuardedCallback = false;
1818
export const enableProfilerTimer = __PROFILE__;
1919
export const enableProfilerCommitHooks = false;
2020
export const enableProfilerNestedUpdatePhase = false;
21+
export const enableProfilerNestedUpdateScheduledHook = false;
2122
export const enableSchedulerTracing = __PROFILE__;
2223
export const enableSuspenseServerRenderer = false;
2324
export const enableSelectiveHydration = false;

‎packages/shared/forks/ReactFeatureFlags.test-renderer.native.js

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export const replayFailedUnitOfWorkWithInvokeGuardedCallback = false;
1818
export const enableProfilerTimer = __PROFILE__;
1919
export const enableProfilerCommitHooks = false;
2020
export const enableProfilerNestedUpdatePhase = false;
21+
export const enableProfilerNestedUpdateScheduledHook = false;
2122
export const enableSchedulerTracing = __PROFILE__;
2223
export const enableSuspenseServerRenderer = false;
2324
export const enableSelectiveHydration = false;

‎packages/shared/forks/ReactFeatureFlags.test-renderer.www.js

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export const replayFailedUnitOfWorkWithInvokeGuardedCallback = false;
1818
export const enableProfilerTimer = __PROFILE__;
1919
export const enableProfilerCommitHooks = false;
2020
export const enableProfilerNestedUpdatePhase = false;
21+
export const enableProfilerNestedUpdateScheduledHook = false;
2122
export const enableSchedulerTracing = __PROFILE__;
2223
export const enableSuspenseServerRenderer = false;
2324
export const enableSelectiveHydration = false;

‎packages/shared/forks/ReactFeatureFlags.testing.js

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export const replayFailedUnitOfWorkWithInvokeGuardedCallback = false;
1818
export const enableProfilerTimer = __PROFILE__;
1919
export const enableProfilerCommitHooks = false;
2020
export const enableProfilerNestedUpdatePhase = false;
21+
export const enableProfilerNestedUpdateScheduledHook = false;
2122
export const enableSchedulerTracing = __PROFILE__;
2223
export const enableSuspenseServerRenderer = false;
2324
export const enableSelectiveHydration = false;

‎packages/shared/forks/ReactFeatureFlags.testing.www.js

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export const replayFailedUnitOfWorkWithInvokeGuardedCallback = false;
1818
export const enableProfilerTimer = false;
1919
export const enableProfilerCommitHooks = false;
2020
export const enableProfilerNestedUpdatePhase = false;
21+
export const enableProfilerNestedUpdateScheduledHook = false;
2122
export const enableSchedulerTracing = false;
2223
export const enableSuspenseServerRenderer = true;
2324
export const enableSelectiveHydration = true;

‎packages/shared/forks/ReactFeatureFlags.www-dynamic.js

+2
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,5 @@ export const disableSchedulerTimeoutBasedOnReactExpirationTime = false;
4848

4949
export const enableDoubleInvokingEffects = false;
5050
export const enableUseRefAccessWarning = __VARIANT__;
51+
52+
export const enableProfilerNestedUpdateScheduledHook = __VARIANT__;

‎packages/shared/forks/ReactFeatureFlags.www.js

+2
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ export const {
3636
export const enableProfilerTimer = __PROFILE__;
3737
export const enableProfilerCommitHooks = __PROFILE__;
3838
export const enableProfilerNestedUpdatePhase = __PROFILE__;
39+
export const enableProfilerNestedUpdateScheduledHook =
40+
__PROFILE__ && dynamicFeatureFlags.enableProfilerNestedUpdateScheduledHook;
3941

4042
// Logs additional User Timing API marks for use with an experimental profiling tool.
4143
export const enableSchedulingProfiler = __PROFILE__;

0 commit comments

Comments
 (0)
Please sign in to comment.