Skip to content

Commit e2fbd8f

Browse files
committed
Fix Missing key Validation in React.Children (#29675)
## Summary In #29088, the validation logic for `React.Children` inspected whether `mappedChild` — the return value of the map callback — has a valid `key`. However, this deviates from existing behavior which only warns if the original `child` is missing a required `key`. This fixes false positive `key` validation warnings when using `React.Children`, by validating the original `child` instead of `mappedChild`. This is a more general fix that expands upon my previous fix in #29662. ## How did you test this change? ``` $ yarn test ReactChildren-test.js ``` DiffTrain build for [8fd963a](8fd963a)
1 parent 5388c9f commit e2fbd8f

32 files changed

+78
-68
lines changed

compiled/facebook-www/REVISION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
aa3d6c0840357eb469df9bd1c20b201197ce3bdc
1+
8fd963a1e5ec89459cac27fb1d9ad193a0604110
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
aa3d6c0840357eb469df9bd1c20b201197ce3bdc
1+
8fd963a1e5ec89459cac27fb1d9ad193a0604110

compiled/facebook-www/React-dev.classic.js

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ if (
2222
) {
2323
__REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(new Error());
2424
}
25-
var ReactVersion = '19.0.0-www-classic-aa3d6c0840-20240530';
25+
var ReactVersion = '19.0.0-www-classic-8fd963a1e5-20240530';
2626

2727
// Re-export dynamic flags from the www version.
2828
var dynamicFeatureFlags = require('ReactFeatureFlags');
@@ -2330,11 +2330,16 @@ function mapIntoArray(children, array, escapedPrefix, nameSoFar, callback) {
23302330
) + '/' : '') + childKey);
23312331

23322332
{
2333-
if (nameSoFar !== '' && mappedChild.key == null) {
2334-
// We need to validate that this child should have had a key before assigning it one.
2335-
if (!newChild._store.validated) {
2336-
// We mark this child as having failed validation but we let the actual renderer
2337-
// print the warning later.
2333+
// If `child` was an element without a `key`, we need to validate if
2334+
// it should have had a `key`, before assigning one to `mappedChild`.
2335+
// $FlowFixMe[incompatible-type] Flow incorrectly thinks React.Portal doesn't have a key
2336+
if (nameSoFar !== '' && _child != null && isValidElement(_child) && _child.key == null) {
2337+
// We check truthiness of `child._store.validated` instead of being
2338+
// inequal to `1` to provide a bit of backward compatibility for any
2339+
// libraries (like `fbt`) which may be hacking this property.
2340+
if (_child._store && !_child._store.validated) {
2341+
// Mark this child as having failed validation, but let the actual
2342+
// renderer print the warning later.
23382343
newChild._store.validated = 2;
23392344
}
23402345
}

compiled/facebook-www/React-dev.modern.js

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ if (
2222
) {
2323
__REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(new Error());
2424
}
25-
var ReactVersion = '19.0.0-www-modern-aa3d6c0840-20240530';
25+
var ReactVersion = '19.0.0-www-modern-8fd963a1e5-20240530';
2626

2727
// Re-export dynamic flags from the www version.
2828
var dynamicFeatureFlags = require('ReactFeatureFlags');
@@ -2333,11 +2333,16 @@ function mapIntoArray(children, array, escapedPrefix, nameSoFar, callback) {
23332333
) + '/' : '') + childKey);
23342334

23352335
{
2336-
if (nameSoFar !== '' && mappedChild.key == null) {
2337-
// We need to validate that this child should have had a key before assigning it one.
2338-
if (!newChild._store.validated) {
2339-
// We mark this child as having failed validation but we let the actual renderer
2340-
// print the warning later.
2336+
// If `child` was an element without a `key`, we need to validate if
2337+
// it should have had a `key`, before assigning one to `mappedChild`.
2338+
// $FlowFixMe[incompatible-type] Flow incorrectly thinks React.Portal doesn't have a key
2339+
if (nameSoFar !== '' && _child != null && isValidElement(_child) && _child.key == null) {
2340+
// We check truthiness of `child._store.validated` instead of being
2341+
// inequal to `1` to provide a bit of backward compatibility for any
2342+
// libraries (like `fbt`) which may be hacking this property.
2343+
if (_child._store && !_child._store.validated) {
2344+
// Mark this child as having failed validation, but let the actual
2345+
// renderer print the warning later.
23412346
newChild._store.validated = 2;
23422347
}
23432348
}

compiled/facebook-www/React-prod.classic.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -684,4 +684,4 @@ exports.useSyncExternalStore = function (
684684
exports.useTransition = function () {
685685
return ReactSharedInternals.H.useTransition();
686686
};
687-
exports.version = "19.0.0-www-classic-aa3d6c0840-20240530";
687+
exports.version = "19.0.0-www-classic-8fd963a1e5-20240530";

compiled/facebook-www/React-prod.modern.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -684,4 +684,4 @@ exports.useSyncExternalStore = function (
684684
exports.useTransition = function () {
685685
return ReactSharedInternals.H.useTransition();
686686
};
687-
exports.version = "19.0.0-www-modern-aa3d6c0840-20240530";
687+
exports.version = "19.0.0-www-modern-8fd963a1e5-20240530";

compiled/facebook-www/React-profiling.classic.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -688,7 +688,7 @@ exports.useSyncExternalStore = function (
688688
exports.useTransition = function () {
689689
return ReactSharedInternals.H.useTransition();
690690
};
691-
exports.version = "19.0.0-www-classic-aa3d6c0840-20240530";
691+
exports.version = "19.0.0-www-classic-8fd963a1e5-20240530";
692692
"undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ &&
693693
"function" ===
694694
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop &&

compiled/facebook-www/React-profiling.modern.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -688,7 +688,7 @@ exports.useSyncExternalStore = function (
688688
exports.useTransition = function () {
689689
return ReactSharedInternals.H.useTransition();
690690
};
691-
exports.version = "19.0.0-www-modern-aa3d6c0840-20240530";
691+
exports.version = "19.0.0-www-modern-8fd963a1e5-20240530";
692692
"undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ &&
693693
"function" ===
694694
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop &&

compiled/facebook-www/ReactART-dev.classic.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ function _assertThisInitialized(self) {
6060
return self;
6161
}
6262

63-
var ReactVersion = '19.0.0-www-classic-aa3d6c0840-20240530';
63+
var ReactVersion = '19.0.0-www-classic-8fd963a1e5-20240530';
6464

6565
var LegacyRoot = 0;
6666
var ConcurrentRoot = 1;

compiled/facebook-www/ReactART-dev.modern.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ function _assertThisInitialized(self) {
6060
return self;
6161
}
6262

63-
var ReactVersion = '19.0.0-www-modern-aa3d6c0840-20240530';
63+
var ReactVersion = '19.0.0-www-modern-8fd963a1e5-20240530';
6464

6565
var LegacyRoot = 0;
6666
var ConcurrentRoot = 1;

0 commit comments

Comments
 (0)