Skip to content

Commit

Permalink
Add Profiler callback when nested updates are scheduled (#20211)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
Brian Vaughn authored Nov 12, 2020
1 parent 11a2ae3 commit 9403c3b
Show file tree
Hide file tree
Showing 13 changed files with 617 additions and 0 deletions.
38 changes: 38 additions & 0 deletions packages/react-reconciler/src/ReactFiberWorkLoop.new.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
replayFailedUnitOfWorkWithInvokeGuardedCallback,
enableProfilerTimer,
enableProfilerNestedUpdatePhase,
enableProfilerNestedUpdateScheduledHook,
enableSchedulerTracing,
warnAboutUnmockedScheduler,
deferRenderPhaseUpdateToNextBatch,
Expand Down Expand Up @@ -110,6 +111,7 @@ import {
ForwardRef,
MemoComponent,
SimpleMemoComponent,
Profiler,
} from './ReactWorkTags';
import {LegacyRoot} from './ReactRootTags';
import {
Expand Down Expand Up @@ -258,6 +260,10 @@ let workInProgress: Fiber | null = null;
// The lanes we're rendering
let workInProgressRootRenderLanes: Lanes = NoLanes;

// Only used when enableProfilerNestedUpdateScheduledHook is true;
// to track which root is currently committing layout effects.
let rootCommittingMutationOrLayoutEffects: FiberRoot | null = null;

// Stack that allows components to change the render lanes for its subtree
// This is a superset of the lanes we started working on at the root. The only
// case where it's different from `workInProgressRootRenderLanes` is when we
Expand Down Expand Up @@ -509,6 +515,30 @@ export function scheduleUpdateOnFiber(
// Mark that the root has a pending update.
markRootUpdated(root, lane, eventTime);

if (enableProfilerTimer && enableProfilerNestedUpdateScheduledHook) {
if (
executionContext === CommitContext &&
root === rootCommittingMutationOrLayoutEffects
) {
if (fiber.mode & ProfileMode) {
let current = fiber;
while (current !== null) {
if (current.tag === Profiler) {
const {onNestedUpdateScheduled} = current.memoizedProps;
if (typeof onNestedUpdateScheduled === 'function') {
if (enableSchedulerTracing) {
onNestedUpdateScheduled(root.memoizedInteractions);
} else {
onNestedUpdateScheduled();
}
}
}
current = current.return;
}
}
}
}

if (root === workInProgressRoot) {
// Received an update to a tree that's in the middle of rendering. Mark
// that there was an interleaved update work on this root. Unless the
Expand Down Expand Up @@ -1898,6 +1928,10 @@ function commitRootImpl(root, renderPriorityLevel) {
recordCommitTime();
}

if (enableProfilerTimer && enableProfilerNestedUpdateScheduledHook) {
rootCommittingMutationOrLayoutEffects = root;
}

// The next phase is the mutation phase, where we mutate the host tree.
commitMutationEffects(finishedWork, root, renderPriorityLevel);

Expand Down Expand Up @@ -1936,6 +1970,10 @@ function commitRootImpl(root, renderPriorityLevel) {
markLayoutEffectsStopped();
}

if (enableProfilerTimer && enableProfilerNestedUpdateScheduledHook) {
rootCommittingMutationOrLayoutEffects = null;
}

// Tell Scheduler to yield at the end of the frame, so the browser has an
// opportunity to paint.
requestPaint();
Expand Down
40 changes: 40 additions & 0 deletions packages/react-reconciler/src/ReactFiberWorkLoop.old.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
enableProfilerTimer,
enableProfilerCommitHooks,
enableProfilerNestedUpdatePhase,
enableProfilerNestedUpdateScheduledHook,
enableSchedulerTracing,
warnAboutUnmockedScheduler,
deferRenderPhaseUpdateToNextBatch,
Expand Down Expand Up @@ -112,6 +113,7 @@ import {
OffscreenComponent,
LegacyHiddenComponent,
ScopeComponent,
Profiler,
} from './ReactWorkTags';
import {LegacyRoot} from './ReactRootTags';
import {
Expand Down Expand Up @@ -329,6 +331,10 @@ let hasUncaughtError = false;
let firstUncaughtError = null;
let legacyErrorBoundariesThatAlreadyFailed: Set<mixed> | null = null;

// Only used when enableProfilerNestedUpdateScheduledHook is true;
// to track which root is currently committing layout effects.
let rootCommittingMutationOrLayoutEffects: FiberRoot | null = null;

let rootDoesHavePassiveEffects: boolean = false;
let rootWithPendingPassiveEffects: FiberRoot | null = null;
let pendingPassiveEffectsRenderPriority: ReactPriorityLevel = NoSchedulerPriority;
Expand Down Expand Up @@ -533,6 +539,30 @@ export function scheduleUpdateOnFiber(
// Mark that the root has a pending update.
markRootUpdated(root, lane, eventTime);

if (enableProfilerTimer && enableProfilerNestedUpdateScheduledHook) {
if (
executionContext === CommitContext &&
root === rootCommittingMutationOrLayoutEffects
) {
if (fiber.mode & ProfileMode) {
let current = fiber;
while (current !== null) {
if (current.tag === Profiler) {
const {onNestedUpdateScheduled} = current.memoizedProps;
if (typeof onNestedUpdateScheduled === 'function') {
if (enableSchedulerTracing) {
onNestedUpdateScheduled(root.memoizedInteractions);
} else {
onNestedUpdateScheduled();
}
}
}
current = current.return;
}
}
}
}

if (root === workInProgressRoot) {
// Received an update to a tree that's in the middle of rendering. Mark
// that there was an interleaved update work on this root. Unless the
Expand Down Expand Up @@ -2047,6 +2077,12 @@ function commitRootImpl(root, renderPriorityLevel) {
recordCommitTime();
}

if (enableProfilerTimer && enableProfilerNestedUpdateScheduledHook) {
// Track the root here, rather than in commitLayoutEffects(), because of ref setters.
// Updates scheduled during ref detachment should also be flagged.
rootCommittingMutationOrLayoutEffects = root;
}

// The next phase is the mutation phase, where we mutate the host tree.
nextEffect = firstEffect;
do {
Expand Down Expand Up @@ -2112,6 +2148,10 @@ function commitRootImpl(root, renderPriorityLevel) {

nextEffect = null;

if (enableProfilerTimer && enableProfilerNestedUpdateScheduledHook) {
rootCommittingMutationOrLayoutEffects = null;
}

// Tell Scheduler to yield at the end of the frame, so the browser has an
// opportunity to paint.
requestPaint();
Expand Down
Loading

0 comments on commit 9403c3b

Please sign in to comment.