. It was passed a child from Inner. ' +
'See https://react.dev/link/warning-keys for more information.\n' +
' in span (at **)\n' +
- ' in Inner (at **)\n' +
- ' in p (at **)',
+ ' in Inner (at **)' +
+ (gate(flags => flags.enableOwnerStacks) ? '' : '\n in p (at **)'),
);
});
- it('should use the inner displayName in the stack', async () => {
+ it('should use the inner name in the stack', async () => {
const fn = (props, ref) => {
return [];
};
- fn.displayName = 'Inner';
+ Object.defineProperty(fn, 'name', {value: 'Inner'});
const MemoComponent = React.memo(fn);
ReactNoop.render(
@@ -645,8 +645,8 @@ describe('memo', () => {
'\n\nCheck the top-level render call using . It was passed a child from Inner. ' +
'See https://react.dev/link/warning-keys for more information.\n' +
' in span (at **)\n' +
- ' in Inner (at **)\n' +
- ' in p (at **)',
+ ' in Inner (at **)' +
+ (gate(flags => flags.enableOwnerStacks) ? '' : '\n in p (at **)'),
);
});
@@ -667,8 +667,8 @@ describe('memo', () => {
'\n\nCheck the top-level render call using . It was passed a child from Outer. ' +
'See https://react.dev/link/warning-keys for more information.\n' +
' in span (at **)\n' +
- ' in Outer (at **)\n' +
- ' in p (at **)',
+ ' in Outer (at **)' +
+ (gate(flags => flags.enableOwnerStacks) ? '' : '\n in p (at **)'),
);
});
@@ -676,7 +676,7 @@ describe('memo', () => {
const fn = (props, ref) => {
return [];
};
- fn.displayName = 'Inner';
+ Object.defineProperty(fn, 'name', {value: 'Inner'});
const MemoComponent = React.memo(fn);
MemoComponent.displayName = 'Outer';
ReactNoop.render(
@@ -691,8 +691,8 @@ describe('memo', () => {
'\n\nCheck the top-level render call using . It was passed a child from Inner. ' +
'See https://react.dev/link/warning-keys for more information.\n' +
' in span (at **)\n' +
- ' in Inner (at **)\n' +
- ' in p (at **)',
+ ' in Inner (at **)' +
+ (gate(flags => flags.enableOwnerStacks) ? '' : '\n in p (at **)'),
);
});
}
diff --git a/packages/react-server/src/ReactFizzServer.js b/packages/react-server/src/ReactFizzServer.js
index 029671bbda81..6060e3ba6002 100644
--- a/packages/react-server/src/ReactFizzServer.js
+++ b/packages/react-server/src/ReactFizzServer.js
@@ -752,6 +752,7 @@ function getCurrentStackInDEV(): string {
if (currentTaskInDEV === null || currentTaskInDEV.componentStack === null) {
return '';
}
+ // TODO: Support owner based stacks for logs during SSR.
return getStackByComponentStackNode(currentTaskInDEV.componentStack);
}
return '';
diff --git a/packages/react/src/ReactForwardRef.js b/packages/react/src/ReactForwardRef.js
index 3d86241430e3..ad9f5a909036 100644
--- a/packages/react/src/ReactForwardRef.js
+++ b/packages/react/src/ReactForwardRef.js
@@ -68,7 +68,9 @@ export function forwardRef(
// React.forwardRef((props, ref) => {...});
// This kind of inner function is not used elsewhere so the side effect is okay.
if (!render.name && !render.displayName) {
- render.displayName = name;
+ Object.defineProperty(render, 'name', {
+ value: name,
+ });
}
},
});
diff --git a/packages/react/src/ReactMemo.js b/packages/react/src/ReactMemo.js
index 91f4864999d3..2948a28193e8 100644
--- a/packages/react/src/ReactMemo.js
+++ b/packages/react/src/ReactMemo.js
@@ -48,7 +48,9 @@ export function memo(
// React.memo((props) => {...});
// This kind of inner function is not used elsewhere so the side effect is okay.
if (!type.name && !type.displayName) {
- type.displayName = name;
+ Object.defineProperty(type, 'name', {
+ value: name,
+ });
}
},
});
diff --git a/packages/react/src/__tests__/ReactCreateRef-test.js b/packages/react/src/__tests__/ReactCreateRef-test.js
index 34dc5aa3aa68..616c62e00c88 100644
--- a/packages/react/src/__tests__/ReactCreateRef-test.js
+++ b/packages/react/src/__tests__/ReactCreateRef-test.js
@@ -45,8 +45,10 @@ describe('ReactCreateRef', () => {
).toErrorDev(
'Unexpected ref object provided for div. ' +
'Use either a ref-setter function or React.createRef().\n' +
- ' in div (at **)\n' +
- ' in Wrapper (at **)',
+ ' in div (at **)' +
+ (gate(flags => flags.enableOwnerStacks)
+ ? ''
+ : '\n in Wrapper (at **)'),
);
expect(() =>
@@ -60,8 +62,10 @@ describe('ReactCreateRef', () => {
).toErrorDev(
'Unexpected ref object provided for ExampleComponent. ' +
'Use either a ref-setter function or React.createRef().\n' +
- ' in ExampleComponent (at **)\n' +
- ' in Wrapper (at **)',
+ ' in ExampleComponent (at **)' +
+ (gate(flags => flags.enableOwnerStacks)
+ ? ''
+ : '\n in Wrapper (at **)'),
);
});
});
diff --git a/packages/react/src/__tests__/ReactElementValidator-test.internal.js b/packages/react/src/__tests__/ReactElementValidator-test.internal.js
index 6d133fc8d9b9..7cac9d7c8390 100644
--- a/packages/react/src/__tests__/ReactElementValidator-test.internal.js
+++ b/packages/react/src/__tests__/ReactElementValidator-test.internal.js
@@ -142,11 +142,10 @@ describe('ReactElementValidator', () => {
'"key" prop.\n\nCheck the render method of `Component`. See ' +
'https://react.dev/link/warning-keys for more information.\n' +
' in div (at **)\n' +
- // TODO: Because this validates after the div has been mounted, it is part of
- // the parent stack but since owner stacks will switch to owners this goes away again.
- (gate(flags => flags.enableOwnerStacks) ? ' in div (at **)\n' : '') +
' in Component (at **)\n' +
- ' in Parent (at **)\n' +
+ (gate(flags => flags.enableOwnerStacks)
+ ? ''
+ : ' in Parent (at **)\n') +
' in GrandParent (at **)',
);
});
@@ -262,8 +261,6 @@ describe('ReactElementValidator', () => {
'Each child in a list should have a unique "key" prop.' +
'\n\nCheck the render method of `ParentComp`. It was passed a child from MyComp. ' +
'See https://react.dev/link/warning-keys for more information.\n' +
- // TODO: Because this validates after the div has been mounted, it is part of
- // the parent stack but since owner stacks will switch to owners this goes away again.
' in div (at **)\n' +
' in MyComp (at **)\n' +
' in ParentComp (at **)',
diff --git a/packages/react/src/__tests__/ReactJSXElementValidator-test.js b/packages/react/src/__tests__/ReactJSXElementValidator-test.js
index bbafbc74011a..7ac4ead790f6 100644
--- a/packages/react/src/__tests__/ReactJSXElementValidator-test.js
+++ b/packages/react/src/__tests__/ReactJSXElementValidator-test.js
@@ -209,8 +209,6 @@ describe('ReactJSXElementValidator', () => {
'Each child in a list should have a unique "key" prop.' +
'\n\nCheck the render method of `ParentComp`. It was passed a child from MyComp. ' +
'See https://react.dev/link/warning-keys for more information.\n' +
- // TODO: Because this validates after the div has been mounted, it is part of
- // the parent stack but since owner stacks will switch to owners this goes away again.
' in div (at **)\n' +
' in MyComp (at **)\n' +
' in ParentComp (at **)',
diff --git a/packages/react/src/__tests__/ReactJSXRuntime-test.js b/packages/react/src/__tests__/ReactJSXRuntime-test.js
index ae26e133fb17..6e6c111589e3 100644
--- a/packages/react/src/__tests__/ReactJSXRuntime-test.js
+++ b/packages/react/src/__tests__/ReactJSXRuntime-test.js
@@ -299,10 +299,9 @@ describe('ReactJSXRuntime', () => {
}).toErrorDev(
'Warning: Each child in a list should have a unique "key" prop.\n\n' +
'Check the render method of `Parent`. See https://react.dev/link/warning-keys for more information.\n' +
- ' in Child (at **)\n' +
- // TODO: Because this validates after the div has been mounted, it is part of
- // the parent stack but since owner stacks will switch to owners this goes away again.
- (gate(flags => flags.enableOwnerStacks) ? ' in div (at **)\n' : '') +
+ (gate(flags => flags.enableOwnerStacks)
+ ? ''
+ : ' in Child (at **)\n') +
' in Parent (at **)',
);
});
diff --git a/packages/react/src/__tests__/createReactClassIntegration-test.js b/packages/react/src/__tests__/createReactClassIntegration-test.js
index 0b65b1425781..cfdc7174c7ba 100644
--- a/packages/react/src/__tests__/createReactClassIntegration-test.js
+++ b/packages/react/src/__tests__/createReactClassIntegration-test.js
@@ -594,6 +594,7 @@ describe('create-react-class-integration', () => {
return null;
},
});
+ Component.displayName = 'Component';
await expect(async () => {
await expect(async () => {
@@ -643,6 +644,7 @@ describe('create-react-class-integration', () => {
return null;
},
});
+ Component.displayName = 'Component';
await expect(async () => {
await expect(async () => {
diff --git a/packages/react/src/__tests__/forwardRef-test.js b/packages/react/src/__tests__/forwardRef-test.js
index 96b0ee96d2b1..fdf6d20ae0aa 100644
--- a/packages/react/src/__tests__/forwardRef-test.js
+++ b/packages/react/src/__tests__/forwardRef-test.js
@@ -197,7 +197,7 @@ describe('forwardRef', () => {
'\n\nCheck the top-level render call using . It was passed a child from ForwardRef. ' +
'See https://react.dev/link/warning-keys for more information.\n' +
' in span (at **)\n' +
- ' in p (at **)',
+ ' in ',
);
});
@@ -217,16 +217,16 @@ describe('forwardRef', () => {
'\n\nCheck the top-level render call using . It was passed a child from ForwardRef(Inner). ' +
'See https://react.dev/link/warning-keys for more information.\n' +
' in span (at **)\n' +
- ' in Inner (at **)\n' +
- ' in p (at **)',
+ ' in Inner (at **)' +
+ (gate(flags => flags.enableOwnerStacks) ? '' : '\n in p (at **)'),
);
});
- it('should use the inner displayName in the stack', async () => {
+ it('should use the inner name in the stack', async () => {
const fn = (props, ref) => {
return [];
};
- fn.displayName = 'Inner';
+ Object.defineProperty(fn, 'name', {value: 'Inner'});
const RefForwardingComponent = React.forwardRef(fn);
ReactNoop.render(
@@ -240,8 +240,8 @@ describe('forwardRef', () => {
'\n\nCheck the top-level render call using . It was passed a child from ForwardRef(Inner). ' +
'See https://react.dev/link/warning-keys for more information.\n' +
' in span (at **)\n' +
- ' in Inner (at **)\n' +
- ' in p (at **)',
+ ' in Inner (at **)' +
+ (gate(flags => flags.enableOwnerStacks) ? '' : '\n in p (at **)'),
);
});
@@ -262,16 +262,16 @@ describe('forwardRef', () => {
'\n\nCheck the top-level render call using . It was passed a child from Outer. ' +
'See https://react.dev/link/warning-keys for more information.\n' +
' in span (at **)\n' +
- ' in Outer (at **)\n' +
- ' in p (at **)',
+ ' in Outer (at **)' +
+ (gate(flags => flags.enableOwnerStacks) ? '' : '\n in p (at **)'),
);
});
- it('should prefer the inner to the outer displayName in the stack', async () => {
+ it('should prefer the inner name to the outer displayName in the stack', async () => {
const fn = (props, ref) => {
return [];
};
- fn.displayName = 'Inner';
+ Object.defineProperty(fn, 'name', {value: 'Inner'});
const RefForwardingComponent = React.forwardRef(fn);
RefForwardingComponent.displayName = 'Outer';
ReactNoop.render(
@@ -286,8 +286,8 @@ describe('forwardRef', () => {
'\n\nCheck the top-level render call using . It was passed a child from Outer. ' +
'See https://react.dev/link/warning-keys for more information.\n' +
' in span (at **)\n' +
- ' in Inner (at **)\n' +
- ' in p (at **)',
+ ' in Inner (at **)' +
+ (gate(flags => flags.enableOwnerStacks) ? '' : '\n in p (at **)'),
);
});
diff --git a/packages/react/src/jsx/ReactJSXElement.js b/packages/react/src/jsx/ReactJSXElement.js
index f76a84f899ab..a5a9880b05b9 100644
--- a/packages/react/src/jsx/ReactJSXElement.js
+++ b/packages/react/src/jsx/ReactJSXElement.js
@@ -492,7 +492,16 @@ export function jsxProdSignatureRunningInDevWithDynamicChildren(
) {
if (__DEV__) {
const isStaticChildren = false;
- return jsxDEV(type, config, maybeKey, isStaticChildren, source, self);
+ return jsxDEVImpl(
+ type,
+ config,
+ maybeKey,
+ isStaticChildren,
+ source,
+ self,
+ __DEV__ && enableOwnerStacks ? Error('react-stack-top-frame') : undefined,
+ __DEV__ && enableOwnerStacks ? createTask(getTaskName(type)) : undefined,
+ );
}
}
@@ -505,7 +514,16 @@ export function jsxProdSignatureRunningInDevWithStaticChildren(
) {
if (__DEV__) {
const isStaticChildren = true;
- return jsxDEV(type, config, maybeKey, isStaticChildren, source, self);
+ return jsxDEVImpl(
+ type,
+ config,
+ maybeKey,
+ isStaticChildren,
+ source,
+ self,
+ __DEV__ && enableOwnerStacks ? Error('react-stack-top-frame') : undefined,
+ __DEV__ && enableOwnerStacks ? createTask(getTaskName(type)) : undefined,
+ );
}
}
@@ -518,6 +536,28 @@ const didWarnAboutKeySpread = {};
* @param {string} key
*/
export function jsxDEV(type, config, maybeKey, isStaticChildren, source, self) {
+ return jsxDEVImpl(
+ type,
+ config,
+ maybeKey,
+ isStaticChildren,
+ source,
+ self,
+ __DEV__ && enableOwnerStacks ? Error('react-stack-top-frame') : undefined,
+ __DEV__ && enableOwnerStacks ? createTask(getTaskName(type)) : undefined,
+ );
+}
+
+function jsxDEVImpl(
+ type,
+ config,
+ maybeKey,
+ isStaticChildren,
+ source,
+ self,
+ debugStack,
+ debugTask,
+) {
if (__DEV__) {
if (!isValidElementType(type)) {
// This is an invalid element type.
@@ -716,8 +756,8 @@ export function jsxDEV(type, config, maybeKey, isStaticChildren, source, self) {
source,
getOwner(),
props,
- __DEV__ && enableOwnerStacks ? Error('react-stack-top-frame') : undefined,
- __DEV__ && enableOwnerStacks ? createTask(getTaskName(type)) : undefined,
+ debugStack,
+ debugTask,
);
}
}
diff --git a/packages/shared/ReactElementType.js b/packages/shared/ReactElementType.js
index 6136f1ac651a..6860932176ee 100644
--- a/packages/shared/ReactElementType.js
+++ b/packages/shared/ReactElementType.js
@@ -9,10 +9,6 @@
import type {ReactDebugInfo} from './ReactTypes';
-interface ConsoleTask {
- run(f: () => T): T;
-}
-
export type ReactElement = {
$$typeof: any,
type: any,
diff --git a/scripts/flow/environment.js b/scripts/flow/environment.js
index 88683e7ee86c..f71e5bba2a1e 100644
--- a/scripts/flow/environment.js
+++ b/scripts/flow/environment.js
@@ -29,6 +29,10 @@ declare module 'create-react-class' {
declare const exports: React$CreateClass;
}
+declare interface ConsoleTask {
+ run(f: () => T): T;
+}
+
// Flow hides the props of React$Element, this overrides it to unhide
// them for React internals.
// prettier-ignore
diff --git a/scripts/jest/matchers/toWarnDev.js b/scripts/jest/matchers/toWarnDev.js
index 50267cc2010c..1114b65dfa82 100644
--- a/scripts/jest/matchers/toWarnDev.js
+++ b/scripts/jest/matchers/toWarnDev.js
@@ -16,6 +16,11 @@ function normalizeCodeLocInfo(str) {
// React format:
// in Component (at filename.js:123)
return str.replace(/\n +(?:at|in) ([\S]+)[^\n]*/g, function (m, name) {
+ if (name.endsWith('.render')) {
+ // Class components will have the `render` method as part of their stack trace.
+ // We strip that out in our normalization to make it look more like component stacks.
+ name = name.slice(0, name.length - 7);
+ }
return '\n in ' + name + ' (at **)';
});
}