Use getComponentNameFromType for debug info for the key warning (#27930)

If this is a client reference we shouldn't dot into it, which would
throw in the proxy.

Interestingly our client references don't really have a `name`
associated with them for debug information so a component type doesn't
show up in error logs even though it seems like it should.
This commit is contained in:
Sebastian Markbåge
2024-01-11 17:24:26 -05:00
committed by GitHub
parent 6639ed3b3a
commit 0ac3ea471f
5 changed files with 55 additions and 16 deletions

View File

@@ -1009,6 +1009,22 @@ describe('ReactFlight', () => {
ReactNoopFlightClient.read(transport);
});
it('should warn in DEV a child is missing keys', () => {
function ParentClient({children}) {
return children;
}
const Parent = clientReference(ParentClient);
expect(() => {
const transport = ReactNoopFlightServer.render(
<Parent>{Array(6).fill(<div>no key</div>)}</Parent>,
);
ReactNoopFlightClient.read(transport);
}).toErrorDev(
'Each child in a list should have a unique "key" prop. ' +
'See https://reactjs.org/link/warning-keys for more information.',
);
});
it('should error if a class instance is passed to a host component', () => {
class Foo {
method() {}

View File

@@ -590,6 +590,29 @@ describe('ReactFlightDOMBrowser', () => {
expect(reportedErrors).toEqual(['for reasons']);
});
it('should warn in DEV a child is missing keys', async () => {
function ParentClient({children}) {
return children;
}
const Parent = clientExports(ParentClient);
const ParentModule = clientExports({Parent: ParentClient});
await expect(async () => {
const stream = ReactServerDOMServer.renderToReadableStream(
<>
<Parent>{Array(6).fill(<div>no key</div>)}</Parent>
<ParentModule.Parent>
{Array(6).fill(<div>no key</div>)}
</ParentModule.Parent>
</>,
webpackMap,
);
await ReactServerDOMClient.createFromReadableStream(stream);
}).toErrorDev(
'Each child in a list should have a unique "key" prop. ' +
'See https://reactjs.org/link/warning-keys for more information.',
);
});
it('basic use(promise)', async () => {
function Server() {
return (

View File

@@ -96,10 +96,7 @@ function getCurrentComponentErrorInfo(parentType) {
let info = getDeclarationErrorAddendum();
if (!info) {
const parentName =
typeof parentType === 'string'
? parentType
: parentType.displayName || parentType.name;
const parentName = getComponentNameFromType(parentType);
if (parentName) {
info = `\n\nCheck the top-level render call using <${parentName}>.`;
}

View File

@@ -108,10 +108,7 @@ function getCurrentComponentErrorInfo(parentType) {
let info = getDeclarationErrorAddendum();
if (!info) {
const parentName =
typeof parentType === 'string'
? parentType
: parentType.displayName || parentType.name;
const parentName = getComponentNameFromType(parentType);
if (parentName) {
info = `\n\nCheck the top-level render call using <${parentName}>.`;
}

View File

@@ -52,21 +52,19 @@ function getContextName(type: ReactContext<any>) {
return type.displayName || 'Context';
}
const REACT_CLIENT_REFERENCE = Symbol.for('react.client.reference');
// Note that the reconciler package should generally prefer to use getComponentNameFromFiber() instead.
export default function getComponentNameFromType(type: mixed): string | null {
if (type == null) {
// Host root, text node or just invalid type.
return null;
}
if (__DEV__) {
if (typeof (type: any).tag === 'number') {
console.error(
'Received an unexpected object in getComponentNameFromType(). ' +
'This is likely a bug in React. Please file an issue.',
);
}
}
if (typeof type === 'function') {
if ((type: any).$$typeof === REACT_CLIENT_REFERENCE) {
// TODO: Create a convention for naming client references with debug info.
return null;
}
return (type: any).displayName || type.name || null;
}
if (typeof type === 'string') {
@@ -96,6 +94,14 @@ export default function getComponentNameFromType(type: mixed): string | null {
}
}
if (typeof type === 'object') {
if (__DEV__) {
if (typeof (type: any).tag === 'number') {
console.error(
'Received an unexpected object in getComponentNameFromType(). ' +
'This is likely a bug in React. Please file an issue.',
);
}
}
switch (type.$$typeof) {
case REACT_CONTEXT_TYPE:
const context: ReactContext<any> = (type: any);