Skip to content

Commit

Permalink
[Fiber] Mark cascading updates (#31866)
Browse files Browse the repository at this point in the history
A common source of performance problems is due to cascading renders from
calling `setState` in `useLayoutEffect` or `useEffect`. This marks the
entry from the update to when we start the render as red and `"Cascade"`
to highlight this.

<img width="964" alt="Screenshot 2024-12-19 at 10 54 59 PM"
src="https://github.com/user-attachments/assets/2bfa91e6-1dc1-4b7f-a659-50aaf2a97e83"
/>

In addition to this case, there's another case where you call `setState`
multiple times in the same event causing multiple renders. This might be
due to multiple `flushSync`, or spawned a microtasks from a
`useLayoutEffect`. In theory it could also be from a microtask scheduled
after the first `setState`. This one we can only detect if it's from an
event that has a `window.event` since otherwise it's hard to know if
we're still in the same event.

<img width="1210" alt="Screenshot 2024-12-19 at 11 38 44 PM"
src="https://github.com/user-attachments/assets/ee188bc4-8ebb-4e95-b5a5-4d724856c27d"
/>

I decided against making a ping in a microtask considered a cascade.
Because that should ideally be using the Suspense Optimization and so
wouldn't be considered multi-pass.

<img width="1284" alt="Screenshot 2024-12-19 at 11 07 30 PM"
src="https://github.com/user-attachments/assets/2d173750-a475-41a0-b6cf-679d15c4ca97"
/>

We might consider making the whole render phase and maybe commit phase
red but that should maybe reserved for actual errors. The "Blocked"
phase really represents the `setState` and so will have the stack trace
of the first update.

DiffTrain build for [1e9eb95](1e9eb95)
  • Loading branch information
sebmarkbage committed Jan 2, 2025
1 parent e34e27e commit 4fe69bf
Show file tree
Hide file tree
Showing 32 changed files with 304 additions and 224 deletions.
2 changes: 1 addition & 1 deletion compiled/facebook-www/React-dev.classic.js
Original file line number Diff line number Diff line change
Expand Up @@ -1942,7 +1942,7 @@ __DEV__ &&
exports.useTransition = function () {
return resolveDispatcher().useTransition();
};
exports.version = "19.1.0-www-classic-fe21c947-20250102";
exports.version = "19.1.0-www-classic-1e9eb95d-20250102";
"undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ &&
"function" ===
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop &&
Expand Down
2 changes: 1 addition & 1 deletion compiled/facebook-www/React-dev.modern.js
Original file line number Diff line number Diff line change
Expand Up @@ -1942,7 +1942,7 @@ __DEV__ &&
exports.useTransition = function () {
return resolveDispatcher().useTransition();
};
exports.version = "19.1.0-www-modern-fe21c947-20250102";
exports.version = "19.1.0-www-modern-1e9eb95d-20250102";
"undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ &&
"function" ===
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop &&
Expand Down
2 changes: 1 addition & 1 deletion compiled/facebook-www/React-prod.classic.js
Original file line number Diff line number Diff line change
Expand Up @@ -630,4 +630,4 @@ exports.useSyncExternalStore = function (
exports.useTransition = function () {
return ReactSharedInternals.H.useTransition();
};
exports.version = "19.1.0-www-classic-fe21c947-20250102";
exports.version = "19.1.0-www-classic-1e9eb95d-20250102";
2 changes: 1 addition & 1 deletion compiled/facebook-www/React-prod.modern.js
Original file line number Diff line number Diff line change
Expand Up @@ -630,4 +630,4 @@ exports.useSyncExternalStore = function (
exports.useTransition = function () {
return ReactSharedInternals.H.useTransition();
};
exports.version = "19.1.0-www-modern-fe21c947-20250102";
exports.version = "19.1.0-www-modern-1e9eb95d-20250102";
2 changes: 1 addition & 1 deletion compiled/facebook-www/React-profiling.classic.js
Original file line number Diff line number Diff line change
Expand Up @@ -634,7 +634,7 @@ exports.useSyncExternalStore = function (
exports.useTransition = function () {
return ReactSharedInternals.H.useTransition();
};
exports.version = "19.1.0-www-classic-fe21c947-20250102";
exports.version = "19.1.0-www-classic-1e9eb95d-20250102";
"undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ &&
"function" ===
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop &&
Expand Down
2 changes: 1 addition & 1 deletion compiled/facebook-www/React-profiling.modern.js
Original file line number Diff line number Diff line change
Expand Up @@ -634,7 +634,7 @@ exports.useSyncExternalStore = function (
exports.useTransition = function () {
return ReactSharedInternals.H.useTransition();
};
exports.version = "19.1.0-www-modern-fe21c947-20250102";
exports.version = "19.1.0-www-modern-1e9eb95d-20250102";
"undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ &&
"function" ===
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop &&
Expand Down
21 changes: 11 additions & 10 deletions compiled/facebook-www/ReactART-dev.classic.js
Original file line number Diff line number Diff line change
Expand Up @@ -2289,11 +2289,9 @@ __DEV__ &&
mightHavePendingSyncWork = !0;
null !== ReactSharedInternals.actQueue
? didScheduleMicrotask_act ||
((didScheduleMicrotask_act = !0),
scheduleImmediateTask(processRootScheduleInMicrotask))
((didScheduleMicrotask_act = !0), scheduleImmediateRootScheduleTask())
: didScheduleMicrotask ||
((didScheduleMicrotask = !0),
scheduleImmediateTask(processRootScheduleInMicrotask));
((didScheduleMicrotask = !0), scheduleImmediateRootScheduleTask());
enableDeferRootSchedulingToMicrotask ||
scheduleTaskForRootDuringMicrotask(root, now$1());
}
Expand Down Expand Up @@ -2339,6 +2337,9 @@ __DEV__ &&
isFlushingWork = !1;
}
}
function processRootScheduleInImmediateTask() {
processRootScheduleInMicrotask();
}
function processRootScheduleInMicrotask() {
mightHavePendingSyncWork =
didScheduleMicrotask_act =
Expand Down Expand Up @@ -2477,13 +2478,13 @@ __DEV__ &&
null !== callbackNode &&
cancelCallback$1(callbackNode);
}
function scheduleImmediateTask(cb) {
function scheduleImmediateRootScheduleTask() {
null !== ReactSharedInternals.actQueue &&
ReactSharedInternals.actQueue.push(function () {
cb();
processRootScheduleInMicrotask();
return null;
});
scheduleCallback$3(ImmediatePriority, cb);
scheduleCallback$3(ImmediatePriority, processRootScheduleInImmediateTask);
}
function requestTransitionLane() {
0 === currentEventTransitionLane &&
Expand Down Expand Up @@ -16894,10 +16895,10 @@ __DEV__ &&
(function () {
var internals = {
bundleType: 1,
version: "19.1.0-www-classic-fe21c947-20250102",
version: "19.1.0-www-classic-1e9eb95d-20250102",
rendererPackageName: "react-art",
currentDispatcherRef: ReactSharedInternals,
reconcilerVersion: "19.1.0-www-classic-fe21c947-20250102"
reconcilerVersion: "19.1.0-www-classic-1e9eb95d-20250102"
};
internals.overrideHookState = overrideHookState;
internals.overrideHookStateDeletePath = overrideHookStateDeletePath;
Expand Down Expand Up @@ -16931,7 +16932,7 @@ __DEV__ &&
exports.Shape = Shape;
exports.Surface = Surface;
exports.Text = Text;
exports.version = "19.1.0-www-classic-fe21c947-20250102";
exports.version = "19.1.0-www-classic-1e9eb95d-20250102";
"undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ &&
"function" ===
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop &&
Expand Down
21 changes: 11 additions & 10 deletions compiled/facebook-www/ReactART-dev.modern.js
Original file line number Diff line number Diff line change
Expand Up @@ -2181,11 +2181,9 @@ __DEV__ &&
mightHavePendingSyncWork = !0;
null !== ReactSharedInternals.actQueue
? didScheduleMicrotask_act ||
((didScheduleMicrotask_act = !0),
scheduleImmediateTask(processRootScheduleInMicrotask))
((didScheduleMicrotask_act = !0), scheduleImmediateRootScheduleTask())
: didScheduleMicrotask ||
((didScheduleMicrotask = !0),
scheduleImmediateTask(processRootScheduleInMicrotask));
((didScheduleMicrotask = !0), scheduleImmediateRootScheduleTask());
enableDeferRootSchedulingToMicrotask ||
scheduleTaskForRootDuringMicrotask(root, now$1());
}
Expand Down Expand Up @@ -2231,6 +2229,9 @@ __DEV__ &&
isFlushingWork = !1;
}
}
function processRootScheduleInImmediateTask() {
processRootScheduleInMicrotask();
}
function processRootScheduleInMicrotask() {
mightHavePendingSyncWork =
didScheduleMicrotask_act =
Expand Down Expand Up @@ -2369,13 +2370,13 @@ __DEV__ &&
null !== callbackNode &&
cancelCallback$1(callbackNode);
}
function scheduleImmediateTask(cb) {
function scheduleImmediateRootScheduleTask() {
null !== ReactSharedInternals.actQueue &&
ReactSharedInternals.actQueue.push(function () {
cb();
processRootScheduleInMicrotask();
return null;
});
scheduleCallback$3(ImmediatePriority, cb);
scheduleCallback$3(ImmediatePriority, processRootScheduleInImmediateTask);
}
function requestTransitionLane() {
0 === currentEventTransitionLane &&
Expand Down Expand Up @@ -16657,10 +16658,10 @@ __DEV__ &&
(function () {
var internals = {
bundleType: 1,
version: "19.1.0-www-modern-fe21c947-20250102",
version: "19.1.0-www-modern-1e9eb95d-20250102",
rendererPackageName: "react-art",
currentDispatcherRef: ReactSharedInternals,
reconcilerVersion: "19.1.0-www-modern-fe21c947-20250102"
reconcilerVersion: "19.1.0-www-modern-1e9eb95d-20250102"
};
internals.overrideHookState = overrideHookState;
internals.overrideHookStateDeletePath = overrideHookStateDeletePath;
Expand Down Expand Up @@ -16694,7 +16695,7 @@ __DEV__ &&
exports.Shape = Shape;
exports.Surface = Surface;
exports.Text = Text;
exports.version = "19.1.0-www-modern-fe21c947-20250102";
exports.version = "19.1.0-www-modern-1e9eb95d-20250102";
"undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ &&
"function" ===
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop &&
Expand Down
24 changes: 12 additions & 12 deletions compiled/facebook-www/ReactART-prod.classic.js
Original file line number Diff line number Diff line change
Expand Up @@ -1348,7 +1348,7 @@ function ensureRootIsScheduled(root) {
mightHavePendingSyncWork = !0;
didScheduleMicrotask ||
((didScheduleMicrotask = !0),
scheduleCallback$3(ImmediatePriority, processRootScheduleInMicrotask));
scheduleCallback$3(ImmediatePriority, processRootScheduleInImmediateTask));
enableDeferRootSchedulingToMicrotask ||
scheduleTaskForRootDuringMicrotask(root, now());
}
Expand Down Expand Up @@ -1395,7 +1395,7 @@ function flushSyncWorkAcrossRoots_impl(syncTransitionLanes, onlyLegacy) {
isFlushingWork = !1;
}
}
function processRootScheduleInMicrotask() {
function processRootScheduleInImmediateTask() {
mightHavePendingSyncWork = didScheduleMicrotask = !1;
0 !== currentEventTransitionLane && (currentEventTransitionLane = 0);
for (
Expand Down Expand Up @@ -10777,24 +10777,24 @@ var slice = Array.prototype.slice,
};
return Text;
})(React.Component);
var internals$jscomp$inline_1501 = {
var internals$jscomp$inline_1507 = {
bundleType: 0,
version: "19.1.0-www-classic-fe21c947-20250102",
version: "19.1.0-www-classic-1e9eb95d-20250102",
rendererPackageName: "react-art",
currentDispatcherRef: ReactSharedInternals,
reconcilerVersion: "19.1.0-www-classic-fe21c947-20250102"
reconcilerVersion: "19.1.0-www-classic-1e9eb95d-20250102"
};
if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) {
var hook$jscomp$inline_1502 = __REACT_DEVTOOLS_GLOBAL_HOOK__;
var hook$jscomp$inline_1508 = __REACT_DEVTOOLS_GLOBAL_HOOK__;
if (
!hook$jscomp$inline_1502.isDisabled &&
hook$jscomp$inline_1502.supportsFiber
!hook$jscomp$inline_1508.isDisabled &&
hook$jscomp$inline_1508.supportsFiber
)
try {
(rendererID = hook$jscomp$inline_1502.inject(
internals$jscomp$inline_1501
(rendererID = hook$jscomp$inline_1508.inject(
internals$jscomp$inline_1507
)),
(injectedHook = hook$jscomp$inline_1502);
(injectedHook = hook$jscomp$inline_1508);
} catch (err) {}
}
var Path = Mode$1.Path;
Expand All @@ -10808,4 +10808,4 @@ exports.RadialGradient = RadialGradient;
exports.Shape = TYPES.SHAPE;
exports.Surface = Surface;
exports.Text = Text;
exports.version = "19.1.0-www-classic-fe21c947-20250102";
exports.version = "19.1.0-www-classic-1e9eb95d-20250102";
24 changes: 12 additions & 12 deletions compiled/facebook-www/ReactART-prod.modern.js
Original file line number Diff line number Diff line change
Expand Up @@ -1195,7 +1195,7 @@ function ensureRootIsScheduled(root) {
mightHavePendingSyncWork = !0;
didScheduleMicrotask ||
((didScheduleMicrotask = !0),
scheduleCallback$3(ImmediatePriority, processRootScheduleInMicrotask));
scheduleCallback$3(ImmediatePriority, processRootScheduleInImmediateTask));
enableDeferRootSchedulingToMicrotask ||
scheduleTaskForRootDuringMicrotask(root, now());
}
Expand Down Expand Up @@ -1242,7 +1242,7 @@ function flushSyncWorkAcrossRoots_impl(syncTransitionLanes, onlyLegacy) {
isFlushingWork = !1;
}
}
function processRootScheduleInMicrotask() {
function processRootScheduleInImmediateTask() {
mightHavePendingSyncWork = didScheduleMicrotask = !1;
0 !== currentEventTransitionLane && (currentEventTransitionLane = 0);
for (
Expand Down Expand Up @@ -10493,24 +10493,24 @@ var slice = Array.prototype.slice,
};
return Text;
})(React.Component);
var internals$jscomp$inline_1480 = {
var internals$jscomp$inline_1486 = {
bundleType: 0,
version: "19.1.0-www-modern-fe21c947-20250102",
version: "19.1.0-www-modern-1e9eb95d-20250102",
rendererPackageName: "react-art",
currentDispatcherRef: ReactSharedInternals,
reconcilerVersion: "19.1.0-www-modern-fe21c947-20250102"
reconcilerVersion: "19.1.0-www-modern-1e9eb95d-20250102"
};
if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) {
var hook$jscomp$inline_1481 = __REACT_DEVTOOLS_GLOBAL_HOOK__;
var hook$jscomp$inline_1487 = __REACT_DEVTOOLS_GLOBAL_HOOK__;
if (
!hook$jscomp$inline_1481.isDisabled &&
hook$jscomp$inline_1481.supportsFiber
!hook$jscomp$inline_1487.isDisabled &&
hook$jscomp$inline_1487.supportsFiber
)
try {
(rendererID = hook$jscomp$inline_1481.inject(
internals$jscomp$inline_1480
(rendererID = hook$jscomp$inline_1487.inject(
internals$jscomp$inline_1486
)),
(injectedHook = hook$jscomp$inline_1481);
(injectedHook = hook$jscomp$inline_1487);
} catch (err) {}
}
var Path = Mode$1.Path;
Expand All @@ -10524,4 +10524,4 @@ exports.RadialGradient = RadialGradient;
exports.Shape = TYPES.SHAPE;
exports.Surface = Surface;
exports.Text = Text;
exports.version = "19.1.0-www-modern-fe21c947-20250102";
exports.version = "19.1.0-www-modern-1e9eb95d-20250102";
30 changes: 17 additions & 13 deletions compiled/facebook-www/ReactDOM-dev.classic.js
Original file line number Diff line number Diff line change
Expand Up @@ -4078,11 +4078,9 @@ __DEV__ &&
mightHavePendingSyncWork = !0;
null !== ReactSharedInternals.actQueue
? didScheduleMicrotask_act ||
((didScheduleMicrotask_act = !0),
scheduleImmediateTask(processRootScheduleInMicrotask))
((didScheduleMicrotask_act = !0), scheduleImmediateRootScheduleTask())
: didScheduleMicrotask ||
((didScheduleMicrotask = !0),
scheduleImmediateTask(processRootScheduleInMicrotask));
((didScheduleMicrotask = !0), scheduleImmediateRootScheduleTask());
enableDeferRootSchedulingToMicrotask ||
scheduleTaskForRootDuringMicrotask(root, now$1());
}
Expand Down Expand Up @@ -4128,6 +4126,9 @@ __DEV__ &&
isFlushingWork = !1;
}
}
function processRootScheduleInImmediateTask() {
processRootScheduleInMicrotask();
}
function processRootScheduleInMicrotask() {
mightHavePendingSyncWork =
didScheduleMicrotask_act =
Expand Down Expand Up @@ -4272,16 +4273,19 @@ __DEV__ &&
null !== callbackNode &&
cancelCallback$1(callbackNode);
}
function scheduleImmediateTask(cb) {
function scheduleImmediateRootScheduleTask() {
null !== ReactSharedInternals.actQueue &&
ReactSharedInternals.actQueue.push(function () {
cb();
processRootScheduleInMicrotask();
return null;
});
scheduleMicrotask(function () {
(executionContext & (RenderContext | CommitContext)) !== NoContext
? scheduleCallback$3(ImmediatePriority, cb)
: cb();
? scheduleCallback$3(
ImmediatePriority,
processRootScheduleInImmediateTask
)
: processRootScheduleInMicrotask();
});
}
function requestTransitionLane() {
Expand Down Expand Up @@ -27374,11 +27378,11 @@ __DEV__ &&
return_targetInst = null;
(function () {
var isomorphicReactPackageVersion = React.version;
if ("19.1.0-www-classic-fe21c947-20250102" !== isomorphicReactPackageVersion)
if ("19.1.0-www-classic-1e9eb95d-20250102" !== isomorphicReactPackageVersion)
throw Error(
'Incompatible React versions: The "react" and "react-dom" packages must have the exact same version. Instead got:\n - react: ' +
(isomorphicReactPackageVersion +
"\n - react-dom: 19.1.0-www-classic-fe21c947-20250102\nLearn more: https://react.dev/warnings/version-mismatch")
"\n - react-dom: 19.1.0-www-classic-1e9eb95d-20250102\nLearn more: https://react.dev/warnings/version-mismatch")
);
})();
("function" === typeof Map &&
Expand Down Expand Up @@ -27421,10 +27425,10 @@ __DEV__ &&
!(function () {
var internals = {
bundleType: 1,
version: "19.1.0-www-classic-fe21c947-20250102",
version: "19.1.0-www-classic-1e9eb95d-20250102",
rendererPackageName: "react-dom",
currentDispatcherRef: ReactSharedInternals,
reconcilerVersion: "19.1.0-www-classic-fe21c947-20250102"
reconcilerVersion: "19.1.0-www-classic-1e9eb95d-20250102"
};
internals.overrideHookState = overrideHookState;
internals.overrideHookStateDeletePath = overrideHookStateDeletePath;
Expand Down Expand Up @@ -28022,7 +28026,7 @@ __DEV__ &&
exports.useFormStatus = function () {
return resolveDispatcher().useHostTransitionStatus();
};
exports.version = "19.1.0-www-classic-fe21c947-20250102";
exports.version = "19.1.0-www-classic-1e9eb95d-20250102";
"undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ &&
"function" ===
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop &&
Expand Down
Loading

0 comments on commit 4fe69bf

Please sign in to comment.