[Fiber] Don't throw away the Error object retaining the owner stack (#33976)
We currently throw away the Error once we've used to the owner stack of a Fiber once. This maybe helps a bit with memory and redoing it but we really don't expect most Fibers to hit this at all. It's not very hot. If we throw away the Error, then we can't use native debugger protocols to inspect the native stack. Instead, we'd have to maintain a url to resource map indefinitely like what Chrome DevTools does to map a url to a resource. Technically it's not even technically correct since the file path might not be reversible and could in theory conflict.
Sebastian Markbåge committed
Jul 24, 2025 at 13:33 UTC
4f34cc4a2e1198493375867d1876509ae9771aee
3 files changed
+6
-9
packages/react-devtools-shared/src/backend/fiber/DevToolsFiberComponentStack.js
+1
-1
@@ -199,7 +199,7 @@ export function getOwnerStackByFiberInDev(
199
if (typeof owner.tag === 'number') {
200
const fiber: Fiber = (owner: any);
201
owner = fiber._debugOwner;
202
- let debugStack = fiber._debugStack;
202
+ let debugStack: void | null | string | Error = fiber._debugStack;
203
// If we don't actually print the stack if there is no owner of this JSX element.
204
// In a real app it's typically not useful since the root app is always controlled
205
// by the framework. These also tend to have noisy stacks because they're not rooted
packages/react-reconciler/src/ReactFiberComponentStack.js
+4
-7
@@ -177,7 +177,7 @@ export function getOwnerStackByFiberInDev(workInProgress: Fiber): string {
177
if (typeof owner.tag === 'number') {
178
const fiber: Fiber = (owner: any);
179
owner = fiber._debugOwner;
180
- let debugStack = fiber._debugStack;
180
+ const debugStack = fiber._debugStack;
181
// If we don't actually print the stack if there is no owner of this JSX element.
182
// In a real app it's typically not useful since the root app is always controlled
183
// by the framework. These also tend to have noisy stacks because they're not rooted
@@ -185,12 +185,9 @@ export function getOwnerStackByFiberInDev(workInProgress: Fiber): string {
185
// if the element was created in module scope. E.g. hoisted. We could add a a single
186
// stack frame for context for example but it doesn't say much if that's a wrapper.
187
if (owner && debugStack) {
188
- if (typeof debugStack !== 'string') {
189
- // Stash the formatted stack so that we can avoid redoing the filtering.
190
- fiber._debugStack = debugStack = formatOwnerStack(debugStack);
191
- }
192
- if (debugStack !== '') {
193
- info += '\n' + debugStack;
188
+ const formattedStack = formatOwnerStack(debugStack);
189
+ if (formattedStack !== '') {
190
+ info += '\n' + formattedStack;
191
}
192
}
193
} else if (owner.debugStack != null) {
packages/react-reconciler/src/ReactInternalTypes.js
+1
-1
@@ -200,7 +200,7 @@ export type Fiber = {
200
201
_debugInfo?: ReactDebugInfo | null,
202
_debugOwner?: ReactComponentInfo | Fiber | null,
203
- _debugStack?: string | Error | null,
203
+ _debugStack?: Error | null,
204
_debugTask?: ConsoleTask | null,
205
_debugNeedsRemount?: boolean,
206