Skip to content

Commit

Permalink
Fix key Warning for Flattened Positional Children (#29662)
Browse files Browse the repository at this point in the history
## 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
```
  • Loading branch information
yungsters committed May 30, 2024
1 parent 51dd096 commit 72644ef
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 1 deletion.
39 changes: 39 additions & 0 deletions packages/react/src/__tests__/ReactChildren-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -868,6 +868,45 @@ describe('ReactChildren', () => {
]);
});

it('should warn for flattened children lists', async () => {
function ComponentRenderingFlattenedChildren({children}) {
return <div>{React.Children.toArray(children)}</div>;
}

const container = document.createElement('div');
const root = ReactDOMClient.createRoot(container);
await expect(async () => {
await act(() => {
root.render(
<ComponentRenderingFlattenedChildren>
{[<div />]}
</ComponentRenderingFlattenedChildren>,
);
});
}).toErrorDev([
'Warning: Each child in a list should have a unique "key" prop.',
]);
});

it('does not warn for flattened positional children', async () => {
function ComponentRenderingFlattenedChildren({children}) {
return <div>{React.Children.toArray(children)}</div>;
}

const container = document.createElement('div');
const root = ReactDOMClient.createRoot(container);
await expect(async () => {
await act(() => {
root.render(
<ComponentRenderingFlattenedChildren>
<div />
<div />
</ComponentRenderingFlattenedChildren>,
);
});
}).toErrorDev([]);
});

it('should escape keys', () => {
const zero = <div key="1" />;
const one = <div key="1=::=2" />;
Expand Down
7 changes: 6 additions & 1 deletion packages/react/src/jsx/ReactJSXElement.js
Original file line number Diff line number Diff line change
Expand Up @@ -953,7 +953,7 @@ export function createElement(type, config, children) {
}

export function cloneAndReplaceKey(oldElement, newKey) {
return ReactElement(
const clonedElement = ReactElement(
oldElement.type,
newKey,
// When enableRefAsProp is on, this argument is ignored. This check only
Expand All @@ -966,6 +966,11 @@ export function cloneAndReplaceKey(oldElement, newKey) {
__DEV__ && enableOwnerStacks ? oldElement._debugStack : undefined,
__DEV__ && enableOwnerStacks ? oldElement._debugTask : undefined,
);
if (__DEV__) {
// The cloned element should inherit the original element's key validation.
clonedElement._store.validated = oldElement._store.validated;
}
return clonedElement;
}

/**
Expand Down

0 comments on commit 72644ef

Please sign in to comment.