Skip to content

Commit ec4b3fc

Browse files
committed
Add owner based debug information to key warning
Now that we have owners in Fizz we can add the missing context in the error message that the client and jsx had. I don't think this context is very useful now that we have good stacks but it is at least parity.
1 parent 2238497 commit ec4b3fc

File tree

2 files changed

+38
-5
lines changed

2 files changed

+38
-5
lines changed

Diff for: packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js

+1-3
Original file line numberDiff line numberDiff line change
@@ -1837,9 +1837,7 @@ describe('ReactDOMFizzServer', () => {
18371837
expect(mockError).toHaveBeenCalledWith(
18381838
'Each child in a list should have a unique "key" prop.%s%s' +
18391839
' See https://react.dev/link/warning-keys for more information.%s',
1840-
gate(flags => flags.enableOwnerStacks)
1841-
? ''
1842-
: '\n\nCheck the render method of `B`.',
1840+
'\n\nCheck the render method of `B`.',
18431841
'',
18441842
'\n' +
18451843
(gate(flags => flags.enableOwnerStacks)

Diff for: packages/react-server/src/ReactFizzServer.js

+37-2
Original file line numberDiff line numberDiff line change
@@ -2922,6 +2922,41 @@ function warnForMissingKey(request: Request, task: Task, child: mixed): void {
29222922
}
29232923
didWarnForKey.add(parentStackFrame);
29242924

2925+
const componentName = getComponentNameFromType(child.type);
2926+
const childOwner = child._owner;
2927+
const parentOwner = parentStackFrame.owner;
2928+
2929+
let currentComponentErrorInfo = '';
2930+
if (parentOwner && typeof parentOwner.tag === 'number') {
2931+
const name = getComponentNameFromType((parentOwner: any).type);
2932+
if (name) {
2933+
currentComponentErrorInfo =
2934+
'\n\nCheck the render method of `' + name + '`.';
2935+
}
2936+
}
2937+
if (!currentComponentErrorInfo) {
2938+
if (componentName) {
2939+
currentComponentErrorInfo = `\n\nCheck the top-level render call using <${componentName}>.`;
2940+
}
2941+
}
2942+
2943+
// Usually the current owner is the offender, but if it accepts children as a
2944+
// property, it may be the creator of the child that's responsible for
2945+
// assigning it a key.
2946+
let childOwnerAppendix = '';
2947+
if (childOwner != null && parentOwner !== childOwner) {
2948+
let ownerName = null;
2949+
if (typeof childOwner.tag === 'number') {
2950+
ownerName = getComponentNameFromType((childOwner: any).type);
2951+
} else if (typeof childOwner.name === 'string') {
2952+
ownerName = childOwner.name;
2953+
}
2954+
if (ownerName) {
2955+
// Give the component that originally created this child.
2956+
childOwnerAppendix = ` It was passed a child from ${ownerName}.`;
2957+
}
2958+
}
2959+
29252960
// We create a fake component stack for the child to log the stack trace from.
29262961
const stackFrame = createComponentStackFromType(
29272962
task,
@@ -2933,8 +2968,8 @@ function warnForMissingKey(request: Request, task: Task, child: mixed): void {
29332968
console.error(
29342969
'Each child in a list should have a unique "key" prop.' +
29352970
'%s%s See https://react.dev/link/warning-keys for more information.',
2936-
'',
2937-
'',
2971+
currentComponentErrorInfo,
2972+
childOwnerAppendix,
29382973
);
29392974
task.componentStack = stackFrame.parent;
29402975
}

0 commit comments

Comments
 (0)