Skip to content

Commit 4c9c109

Browse files
authored
[Fiber] Try to give a stack trace to every entry in the Scheduler Performance Track (facebook#34123)
For "render" and "commit" phases we don't give any specific stack atm. This tries to always provide something useful to say the cause of the render. For normal renders this will now show the same thing as the "Event" and "Update" entries already showed. We stash the task that was used for those and use them throughout the render and commit phases. For Suspense (Retry lane) and Idle (Offscreen lane), we don't have any updates. Instead for those there's a component that left work behind in previous passes. For those I use the debugTask of the `<Suspense>` or `<Activity>` boundary to indicate that this was the root of the render. Similarly when an Action is invoked on a `<form action={...}>` component using the built-in submit handler, there's no actionable stack in user space that called it. So we use the stack of the JSX for the form instead.
1 parent 552a5da commit 4c9c109

File tree

6 files changed

+546
-144
lines changed

6 files changed

+546
-144
lines changed

packages/react-reconciler/src/ReactFiberBeginWork.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ import {
158158
DefaultHydrationLane,
159159
SomeRetryLane,
160160
includesSomeLane,
161+
includesOnlyRetries,
161162
laneToLanes,
162163
removeLanes,
163164
mergeLanes,
@@ -269,6 +270,7 @@ import {
269270
scheduleUpdateOnFiber,
270271
renderDidSuspendDelayIfPossible,
271272
markSkippedUpdateLanes,
273+
markRenderDerivedCause,
272274
getWorkInProgressRoot,
273275
peekDeferredLane,
274276
} from './ReactFiberWorkLoop';
@@ -946,6 +948,13 @@ function updateDehydratedActivityComponent(
946948
// but after we've already committed once.
947949
warnIfHydrating();
948950

951+
if (includesSomeLane(renderLanes, (OffscreenLane: Lane))) {
952+
// If we're rendering Offscreen and we're entering the activity then it's possible
953+
// that the only reason we rendered was because this boundary left work. Provide
954+
// it as a cause if another one doesn't already exist.
955+
markRenderDerivedCause(workInProgress);
956+
}
957+
949958
if (
950959
// TODO: Factoring is a little weird, since we check this right below, too.
951960
!didReceiveUpdate
@@ -1132,6 +1141,16 @@ function updateActivityComponent(
11321141
children: nextChildren,
11331142
};
11341143

1144+
if (
1145+
includesSomeLane(renderLanes, (OffscreenLane: Lane)) &&
1146+
includesSomeLane(renderLanes, current.lanes)
1147+
) {
1148+
// If we're rendering Offscreen and we're entering the activity then it's possible
1149+
// that the only reason we rendered was because this boundary left work. Provide
1150+
// it as a cause if another one doesn't already exist.
1151+
markRenderDerivedCause(workInProgress);
1152+
}
1153+
11351154
const primaryChildFragment = updateWorkInProgressOffscreenFiber(
11361155
currentChild,
11371156
offscreenChildProps,
@@ -2515,6 +2534,17 @@ function updateSuspenseComponent(
25152534
workInProgress.memoizedState = SUSPENDED_MARKER;
25162535
return fallbackChildFragment;
25172536
} else {
2537+
if (
2538+
prevState !== null &&
2539+
includesOnlyRetries(renderLanes) &&
2540+
includesSomeLane(renderLanes, current.lanes)
2541+
) {
2542+
// If we're rendering Retry lanes and we're entering the primary content then it's possible
2543+
// that the only reason we rendered was because we left this boundary to be warmed up but
2544+
// nothing else scheduled an update. If so, use it as the cause of the render.
2545+
markRenderDerivedCause(workInProgress);
2546+
}
2547+
25182548
pushPrimaryTreeSuspenseHandler(workInProgress);
25192549

25202550
const nextPrimaryChildren = nextProps.children;
@@ -2873,6 +2903,13 @@ function updateDehydratedSuspenseComponent(
28732903
// but after we've already committed once.
28742904
warnIfHydrating();
28752905

2906+
if (includesSomeLane(renderLanes, (OffscreenLane: Lane))) {
2907+
// If we're rendering Offscreen and we're entering the activity then it's possible
2908+
// that the only reason we rendered was because this boundary left work. Provide
2909+
// it as a cause if another one doesn't already exist.
2910+
markRenderDerivedCause(workInProgress);
2911+
}
2912+
28762913
if (isSuspenseInstanceFallback(suspenseInstance)) {
28772914
// This boundary is in a permanent fallback state. In this case, we'll never
28782915
// get an update and we'll never be able to hydrate the final content. Let's just try the

packages/react-reconciler/src/ReactFiberHooks.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,10 @@ import {
122122
markStateUpdateScheduled,
123123
setIsStrictModeForDevtools,
124124
} from './ReactFiberDevToolsHook';
125-
import {startUpdateTimerByLane} from './ReactProfilerTimer';
125+
import {
126+
startUpdateTimerByLane,
127+
startHostActionTimer,
128+
} from './ReactProfilerTimer';
126129
import {createCache} from './ReactFiberCacheComponent';
127130
import {
128131
createUpdate as createLegacyQueueUpdate,
@@ -3239,6 +3242,8 @@ export function startHostTransition<F>(
32393242
BasicStateAction<Thenable<TransitionStatus> | TransitionStatus>,
32403243
> = stateHook.queue;
32413244

3245+
startHostActionTimer(formFiber);
3246+
32423247
startTransition(
32433248
formFiber,
32443249
queue,

0 commit comments

Comments
 (0)