Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Use getComponentNameFromType for debug info for the key warning
If this is a client reference we shouldn't dot into it.
  • Loading branch information
sebmarkbage committed Jan 11, 2024
commit c36c5265ff2889383b1dce4d7ea92ff9bc47530a
16 changes: 16 additions & 0 deletions packages/react-client/src/__tests__/ReactFlight-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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() {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down
5 changes: 1 addition & 4 deletions packages/react/src/ReactElementValidator.js
Original file line number Diff line number Diff line change
Expand Up @@ -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}>.`;
}
Expand Down
5 changes: 1 addition & 4 deletions packages/react/src/jsx/ReactJSXElementValidator.js
Original file line number Diff line number Diff line change
Expand Up @@ -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}>.`;
}
Expand Down
22 changes: 14 additions & 8 deletions packages/shared/getComponentNameFromType.js
Original file line number Diff line number Diff line change
Expand Up @@ -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') {
Expand Down Expand Up @@ -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);
Expand Down