Skip to content

Commit 8fe1731

Browse files
committed
Fix Missing key Validation in React.Children
1 parent 9710853 commit 8fe1731

File tree

2 files changed

+109
-2
lines changed

2 files changed

+109
-2
lines changed

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

Lines changed: 102 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -868,7 +868,107 @@ describe('ReactChildren', () => {
868868
]);
869869
});
870870

871-
it('should warn for flattened children lists', async () => {
871+
it('warns for mapped list children without keys', async () => {
872+
function ComponentRenderingMappedChildren({children}) {
873+
return (
874+
<div>
875+
{React.Children.map(children, child => (
876+
<div />
877+
))}
878+
</div>
879+
);
880+
}
881+
882+
const container = document.createElement('div');
883+
const root = ReactDOMClient.createRoot(container);
884+
await expect(async () => {
885+
await act(() => {
886+
root.render(
887+
<ComponentRenderingMappedChildren>
888+
{[<div />]}
889+
</ComponentRenderingMappedChildren>,
890+
);
891+
});
892+
}).toErrorDev([
893+
'Warning: Each child in a list should have a unique "key" prop.',
894+
]);
895+
});
896+
897+
it('does not warn for mapped static children without keys', async () => {
898+
function ComponentRenderingMappedChildren({children}) {
899+
return (
900+
<div>
901+
{React.Children.map(children, child => (
902+
<div />
903+
))}
904+
</div>
905+
);
906+
}
907+
908+
const container = document.createElement('div');
909+
const root = ReactDOMClient.createRoot(container);
910+
await expect(async () => {
911+
await act(() => {
912+
root.render(
913+
<ComponentRenderingMappedChildren>
914+
<div />
915+
<div />
916+
</ComponentRenderingMappedChildren>,
917+
);
918+
});
919+
}).toErrorDev([
920+
'Warning: Each child in a list should have a unique "key" prop.',
921+
]);
922+
});
923+
924+
it('warns for cloned list children without keys', async () => {
925+
function ComponentRenderingClonedChildren({children}) {
926+
return (
927+
<div>
928+
{React.Children.map(children, child => React.cloneElement(child))}
929+
</div>
930+
);
931+
}
932+
933+
const container = document.createElement('div');
934+
const root = ReactDOMClient.createRoot(container);
935+
await expect(async () => {
936+
await act(() => {
937+
root.render(
938+
<ComponentRenderingClonedChildren>
939+
{[<div />]}
940+
</ComponentRenderingClonedChildren>,
941+
);
942+
});
943+
}).toErrorDev([
944+
'Warning: Each child in a list should have a unique "key" prop.',
945+
]);
946+
});
947+
948+
it('does not warn for cloned static children without keys', async () => {
949+
function ComponentRenderingClonedChildren({children}) {
950+
return (
951+
<div>
952+
{React.Children.map(children, child => React.cloneElement(child))}
953+
</div>
954+
);
955+
}
956+
957+
const container = document.createElement('div');
958+
const root = ReactDOMClient.createRoot(container);
959+
await expect(async () => {
960+
await act(() => {
961+
root.render(
962+
<ComponentRenderingClonedChildren>
963+
<div />
964+
<div />
965+
</ComponentRenderingClonedChildren>,
966+
);
967+
});
968+
}).toErrorDev([]);
969+
});
970+
971+
it('warns for flattened list children without keys', async () => {
872972
function ComponentRenderingFlattenedChildren({children}) {
873973
return <div>{React.Children.toArray(children)}</div>;
874974
}
@@ -888,7 +988,7 @@ describe('ReactChildren', () => {
888988
]);
889989
});
890990

891-
it('does not warn for flattened positional children', async () => {
991+
it('does not warn for flattened static children without keys', async () => {
892992
function ComponentRenderingFlattenedChildren({children}) {
893993
return <div>{React.Children.toArray(children)}</div>;
894994
}

packages/react/src/jsx/ReactJSXElement.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1085,6 +1085,13 @@ export function cloneElement(element, config, children) {
10851085
__DEV__ && enableOwnerStacks ? element._debugTask : undefined,
10861086
);
10871087

1088+
if (__DEV__) {
1089+
// The cloned element should inherit the original element's key validation.
1090+
if (element._store) {
1091+
clonedElement._store.validated = element._store.validated;
1092+
}
1093+
}
1094+
10881095
for (let i = 2; i < arguments.length; i++) {
10891096
validateChildKeys(arguments[i], clonedElement.type);
10901097
}

0 commit comments

Comments
 (0)