@samitouri / QOS-React / commits / 72644ef2f2

Fix `key` Warning for Flattened Positional Children (#29662)

## Summary https://github.com/facebook/react/pull/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 ```

Timothy Yung committed May 30, 2024 at 07:25 UTC 72644ef2f2ec7a274f79f6b32320d62757521329
2 files changed +45 -1
packages/react/src/__tests__/ReactChildren-test.js
+39
@@ -868,6 +868,45 @@ describe('ReactChildren', () => {
868 ]);
869 });
870
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 +
910 it('should escape keys', () => {
911 const zero = <div key="1" />;
912 const one = <div key="1=::=2" />;
packages/react/src/jsx/ReactJSXElement.js
+6 -1
@@ -953,7 +953,7 @@ export function createElement(type, config, children) {
953 }
954
955 export function cloneAndReplaceKey(oldElement, newKey) {
956 - return ReactElement(
956 + const clonedElement = ReactElement(
957 oldElement.type,
958 newKey,
959 // When enableRefAsProp is on, this argument is ignored. This check only
@@ -966,6 +966,11 @@ export function cloneAndReplaceKey(oldElement, newKey) {
966 __DEV__ && enableOwnerStacks ? oldElement._debugStack : undefined,
967 __DEV__ && enableOwnerStacks ? oldElement._debugTask : undefined,
968 );
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;
974 }
975
976 /**