@samitouri / QOS-React-2 / commits / b44a99bf58

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

Same as #33723 but for Fiber.

Sebastian Markbåge committed Jul 8, 2025 at 00:00 UTC b44a99bf58d69d52b5288d9eadcc6d226d705e11
2 files changed +43 -3
packages/react-reconciler/src/ReactFiberComponentStack.js
+8 -2
@@ -34,7 +34,7 @@ import {
34 } from 'shared/ReactComponentStackFrame';
35 import {formatOwnerStack} from 'shared/ReactOwnerStackFrames';
36
37 -function describeFiber(fiber: Fiber): string {
37 +function describeFiber(fiber: Fiber, childFiber: null | Fiber): string {
38 switch (fiber.tag) {
39 case HostHoistable:
40 case HostSingleton:
@@ -44,6 +44,10 @@ function describeFiber(fiber: Fiber): string {
44 // TODO: When we support Thenables as component types we should rename this.
45 return describeBuiltInComponentFrame('Lazy');
46 case SuspenseComponent:
47 + if (fiber.child !== childFiber && childFiber !== null) {
48 + // If we came from the second Fiber then we're in the Suspense Fallback.
49 + return describeBuiltInComponentFrame('Suspense Fallback');
50 + }
51 return describeBuiltInComponentFrame('Suspense');
52 case SuspenseListComponent:
53 return describeBuiltInComponentFrame('SuspenseList');
@@ -70,8 +74,9 @@ export function getStackByFiberInDevAndProd(workInProgress: Fiber): string {
74 try {
75 let info = '';
76 let node: Fiber = workInProgress;
77 + let previous: null | Fiber = null;
78 do {
74 - info += describeFiber(node);
79 + info += describeFiber(node, previous);
80 if (__DEV__) {
81 // Add any Server Component stack frames in reverse order.
82 const debugInfo = node._debugInfo;
@@ -88,6 +93,7 @@ export function getStackByFiberInDevAndProd(workInProgress: Fiber): string {
93 }
94 }
95 }
96 + previous = node;
97 // $FlowFixMe[incompatible-type] we bail out when we get a null
98 node = node.return;
99 } while (node);
packages/react-reconciler/src/__tests__/ReactErrorStacks-test.js
+35 -1
@@ -87,7 +87,7 @@ describe('ReactFragment', () => {
87 function normalizeCodeLocInfo(str) {
88 return (
89 str &&
90 - str.replace(/\n +(?:at|in) ([\S]+)[^\n]*/g, function (m, name) {
90 + str.replace(/\n +(?:at|in) ([^\(]+) [^\n]*/g, function (m, name) {
91 return '\n in ' + name + ' (at **)';
92 })
93 );
@@ -168,6 +168,40 @@ describe('ReactFragment', () => {
168 ]);
169 });
170
171 + it('includes built-in for Suspense fallbacks', async () => {
172 + const SomethingThatSuspends = React.lazy(() => {
173 + return new Promise(() => {});
174 + });
175 +
176 + ReactNoop.createRoot({
177 + onCaughtError,
178 + }).render(
179 + <CatchingBoundary>
180 + <Suspense fallback={<SomethingThatErrors />}>
181 + <SomethingThatSuspends />
182 + </Suspense>
183 + </CatchingBoundary>,
184 + );
185 + await waitForAll([]);
186 + expect(didCatchErrors).toEqual([
187 + 'uh oh',
188 + componentStack([
189 + 'SomethingThatErrors',
190 + 'Suspense Fallback',
191 + 'CatchingBoundary',
192 + ]),
193 + ]);
194 + expect(rootCaughtErrors).toEqual([
195 + 'uh oh',
196 + componentStack([
197 + 'SomethingThatErrors',
198 + 'Suspense Fallback',
199 + 'CatchingBoundary',
200 + ]),
201 + __DEV__ ? componentStack(['SomethingThatErrors']) : null,
202 + ]);
203 + });
204 +
205 // @gate enableActivity
206 it('includes built-in for Activity', async () => {
207 ReactNoop.createRoot({