[Fiber] Name content inside "Suspense fallback" (#33724)

Same as #33723 but for Fiber.
This commit is contained in:
Sebastian Markbåge
2025-07-08 00:00:00 -04:00
committed by GitHub
parent e4314a0a0f
commit b44a99bf58
2 changed files with 43 additions and 3 deletions
+8 -2
View File
@@ -34,7 +34,7 @@ import {
} from 'shared/ReactComponentStackFrame';
import {formatOwnerStack} from 'shared/ReactOwnerStackFrames';
function describeFiber(fiber: Fiber): string {
function describeFiber(fiber: Fiber, childFiber: null | Fiber): string {
switch (fiber.tag) {
case HostHoistable:
case HostSingleton:
@@ -44,6 +44,10 @@ function describeFiber(fiber: Fiber): string {
// TODO: When we support Thenables as component types we should rename this.
return describeBuiltInComponentFrame('Lazy');
case SuspenseComponent:
if (fiber.child !== childFiber && childFiber !== null) {
// If we came from the second Fiber then we're in the Suspense Fallback.
return describeBuiltInComponentFrame('Suspense Fallback');
}
return describeBuiltInComponentFrame('Suspense');
case SuspenseListComponent:
return describeBuiltInComponentFrame('SuspenseList');
@@ -70,8 +74,9 @@ export function getStackByFiberInDevAndProd(workInProgress: Fiber): string {
try {
let info = '';
let node: Fiber = workInProgress;
let previous: null | Fiber = null;
do {
info += describeFiber(node);
info += describeFiber(node, previous);
if (__DEV__) {
// Add any Server Component stack frames in reverse order.
const debugInfo = node._debugInfo;
@@ -88,6 +93,7 @@ export function getStackByFiberInDevAndProd(workInProgress: Fiber): string {
}
}
}
previous = node;
// $FlowFixMe[incompatible-type] we bail out when we get a null
node = node.return;
} while (node);
@@ -87,7 +87,7 @@ describe('ReactFragment', () => {
function normalizeCodeLocInfo(str) {
return (
str &&
str.replace(/\n +(?:at|in) ([\S]+)[^\n]*/g, function (m, name) {
str.replace(/\n +(?:at|in) ([^\(]+) [^\n]*/g, function (m, name) {
return '\n in ' + name + ' (at **)';
})
);
@@ -168,6 +168,40 @@ describe('ReactFragment', () => {
]);
});
it('includes built-in for Suspense fallbacks', async () => {
const SomethingThatSuspends = React.lazy(() => {
return new Promise(() => {});
});
ReactNoop.createRoot({
onCaughtError,
}).render(
<CatchingBoundary>
<Suspense fallback={<SomethingThatErrors />}>
<SomethingThatSuspends />
</Suspense>
</CatchingBoundary>,
);
await waitForAll([]);
expect(didCatchErrors).toEqual([
'uh oh',
componentStack([
'SomethingThatErrors',
'Suspense Fallback',
'CatchingBoundary',
]),
]);
expect(rootCaughtErrors).toEqual([
'uh oh',
componentStack([
'SomethingThatErrors',
'Suspense Fallback',
'CatchingBoundary',
]),
__DEV__ ? componentStack(['SomethingThatErrors']) : null,
]);
});
// @gate enableActivity
it('includes built-in for Activity', async () => {
ReactNoop.createRoot({