Skip to content

Commit 72644ef

Browse files
authored
Fix key Warning for Flattened Positional Children (#29662)
## Summary #29088 introduced a regression triggering this warning when rendering flattened positional children: > Each child in a list should have a unique "key" prop. The specific scenario that triggers this is when rendering multiple positional children (which do not require unique `key` props) after flattening them with one of the `React.Children` utilities (e.g. `React.Children.toArray`). The refactored logic in `React.Children` incorrectly drops the `element._store.validated` property in `__DEV__`. This diff fixes the bug and introduces a unit test to prevent future regressions. ## How did you test this change? ``` $ yarn test ReactChildren-test.js ```
1 parent 51dd096 commit 72644ef

File tree

2 files changed

+45
-1
lines changed

2 files changed

+45
-1
lines changed

packages/react/src/__tests__/ReactChildren-test.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -868,6 +868,45 @@ describe('ReactChildren', () => {
868868
]);
869869
});
870870

871+
it('should warn for flattened children lists', async () => {
872+
function ComponentRenderingFlattenedChildren({children}) {
873+
return <div>{React.Children.toArray(children)}</div>;
874+
}
875+
876+
const container = document.createElement('div');
877+
const root = ReactDOMClient.createRoot(container);
878+
await expect(async () => {
879+
await act(() => {
880+
root.render(
881+
<ComponentRenderingFlattenedChildren>
882+
{[<div />]}
883+
</ComponentRenderingFlattenedChildren>,
884+
);
885+
});
886+
}).toErrorDev([
887+
'Warning: Each child in a list should have a unique "key" prop.',
888+
]);
889+
});
890+
891+
it('does not warn for flattened positional children', async () => {
892+
function ComponentRenderingFlattenedChildren({children}) {
893+
return <div>{React.Children.toArray(children)}</div>;
894+
}
895+
896+
const container = document.createElement('div');
897+
const root = ReactDOMClient.createRoot(container);
898+
await expect(async () => {
899+
await act(() => {
900+
root.render(
901+
<ComponentRenderingFlattenedChildren>
902+
<div />
903+
<div />
904+
</ComponentRenderingFlattenedChildren>,
905+
);
906+
});
907+
}).toErrorDev([]);
908+
});
909+
871910
it('should escape keys', () => {
872911
const zero = <div key="1" />;
873912
const one = <div key="1=::=2" />;

packages/react/src/jsx/ReactJSXElement.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -953,7 +953,7 @@ export function createElement(type, config, children) {
953953
}
954954

955955
export function cloneAndReplaceKey(oldElement, newKey) {
956-
return ReactElement(
956+
const clonedElement = ReactElement(
957957
oldElement.type,
958958
newKey,
959959
// When enableRefAsProp is on, this argument is ignored. This check only
@@ -966,6 +966,11 @@ export function cloneAndReplaceKey(oldElement, newKey) {
966966
__DEV__ && enableOwnerStacks ? oldElement._debugStack : undefined,
967967
__DEV__ && enableOwnerStacks ? oldElement._debugTask : undefined,
968968
);
969+
if (__DEV__) {
970+
// The cloned element should inherit the original element's key validation.
971+
clonedElement._store.validated = oldElement._store.validated;
972+
}
973+
return clonedElement;
969974
}
970975

971976
/**

0 commit comments

Comments
 (0)