Split out warning message for nested key warning
Also make sure to mark child array as validated in cloneElement as well.
Ben Alpert committed
May 12, 2015 at 16:33 UTC
64c9d9d7629600da81ceefcaa59c46e64d030874
3 files changed
+38
-27
src/classic/element/ReactElement.js
+19
-15
@@ -80,6 +80,21 @@ function defineMutationMembrane(prototype) {
80
}
81
}
82
83
+function markChildArrayValidated(childArray) {
84
+ // To make comparing ReactElements easier for testing purposes, we make the
85
+ // validation flag non-enumerable (where possible, which should include every
86
+ // environment we run tests in), so the test framework ignores it.
87
+ try {
88
+ Object.defineProperty(childArray, '_reactChildKeysValidated', {
89
+ configurable: false,
90
+ enumerable: false,
91
+ writable: true
92
+ });
93
+ } catch (x) {
94
+ }
95
+ childArray._reactChildKeysValidated = true;
96
+}
97
+
98
/**
99
* Base constructor for all React elements. This is only used to make this
100
* work with a dynamic instanceof check. Nothing should live on this prototype.
@@ -156,23 +171,9 @@ ReactElement.createElement = function(type, config, children) {
171
props.children = children;
172
} else if (childrenLength > 1) {
173
var childArray = Array(childrenLength);
159
-
174
if (__DEV__) {
161
- // To make comparing ReactElements easier for testing purposes, we make
162
- // the validation flag non-enumerable (where possible, which should
163
- // include every environment we run tests in), so the test framework
164
- // ignores it.
165
- try {
166
- Object.defineProperty(childArray, '_reactChildKeysValidated', {
167
- configurable: false,
168
- enumerable: false,
169
- writable: true
170
- });
171
- } catch (x) {
172
- }
173
- childArray._reactChildKeysValidated = true;
175
+ markChildArrayValidated(childArray);
176
}
175
-
177
for (var i = 0; i < childrenLength; i++) {
178
childArray[i] = arguments[i + 2];
179
}
@@ -260,6 +261,9 @@ ReactElement.cloneElement = function(element, config, children) {
261
props.children = children;
262
} else if (childrenLength > 1) {
263
var childArray = Array(childrenLength);
264
+ if (__DEV__) {
265
+ markChildArrayValidated(childArray);
266
+ }
267
for (var i = 0; i < childrenLength; i++) {
268
childArray[i] = arguments[i + 2];
269
}
src/classic/element/ReactElementValidator.js
+17
-10
@@ -90,13 +90,19 @@ function getCurrentOwnerDisplayName() {
90
* @internal
91
* @param {ReactElement} element Element that requires a key.
92
* @param {*} parentType element's parent's type.
93
+ * @param {boolean} deep false if top-level collection, true if nested
94
*/
94
-function validateExplicitKey(element, parentType) {
95
+function validateExplicitKey(element, parentType, deep) {
96
if (element.key != null) {
97
return;
98
}
99
warnAndMonitorForKeyUse(
99
- 'Each child in an array or iterator should have a unique "key" prop.',
100
+ // We vary the message for nested key warning to allow filtering them out
101
+ // since we didn't historically warn in this case.
102
+ deep ?
103
+ 'Each child in a nested array or iterator should have ' +
104
+ 'a unique "key" prop.' :
105
+ 'Each child in an array or iterator should have a unique "key" prop.',
106
element,
107
parentType
108
);
@@ -178,8 +184,9 @@ function warnAndMonitorForKeyUse(message, element, parentType) {
184
* @internal
185
* @param {ReactNode} node Statically passed child of any type.
186
* @param {*} parentType node's parent's type.
187
+ * @param {boolean} deep false if top-level collection, true if nested
188
*/
182
-function validateChildKeys(node, parentType) {
189
+function validateChildKeys(node, parentType, deep) {
190
if (Array.isArray(node)) {
191
if (node._reactChildKeysValidated) {
192
// All child elements were passed in a valid location.
@@ -188,10 +195,10 @@ function validateChildKeys(node, parentType) {
195
for (var i = 0; i < node.length; i++) {
196
var child = node[i];
197
if (ReactElement.isValidElement(child)) {
191
- validateExplicitKey(child, parentType);
198
+ validateExplicitKey(child, parentType, deep);
199
} else {
200
// TODO: Warn on unkeyed arrays and suggest using createFragment
194
- validateChildKeys(child, parentType);
201
+ validateChildKeys(child, parentType, true);
202
}
203
}
204
} else if (
@@ -209,9 +216,9 @@ function validateChildKeys(node, parentType) {
216
var step;
217
while (!(step = iterator.next()).done) {
218
if (ReactElement.isValidElement(step.value)) {
212
- validateExplicitKey(step.value, parentType);
219
+ validateExplicitKey(step.value, parentType, deep);
220
} else {
214
- validateChildKeys(step.value, parentType);
221
+ validateChildKeys(step.value, parentType, true);
222
}
223
}
224
}
@@ -220,7 +227,7 @@ function validateChildKeys(node, parentType) {
227
for (var key in fragment) {
228
if (fragment.hasOwnProperty(key)) {
229
validatePropertyKey(key, fragment[key], parentType);
223
- validateChildKeys(fragment[key], parentType);
230
+ validateChildKeys(fragment[key], parentType, true);
231
}
232
}
233
}
@@ -426,7 +433,7 @@ var ReactElementValidator = {
433
}
434
435
for (var i = 2; i < arguments.length; i++) {
429
- validateChildKeys(arguments[i], type);
436
+ validateChildKeys(arguments[i], type, false);
437
}
438
439
validatePropTypes(element);
@@ -474,7 +481,7 @@ var ReactElementValidator = {
481
cloneElement: function(element, props, children) {
482
var newElement = ReactElement.cloneElement.apply(this, arguments);
483
for (var i = 2; i < arguments.length; i++) {
477
- validateChildKeys(arguments[i], newElement.type);
484
+ validateChildKeys(arguments[i], newElement.type, false);
485
}
486
validatePropTypes(newElement);
487
return newElement;
src/classic/element/__tests__/ReactElementValidator-test.js
+2
-2
@@ -134,8 +134,8 @@ describe('ReactElementValidator', function() {
134
135
expect(console.error.argsForCall.length).toBe(1);
136
expect(console.error.argsForCall[0][0]).toBe(
137
- 'Warning: Each child in an array or iterator should have a unique ' +
138
- '"key" prop. Check the React.render call using <div>. See ' +
137
+ 'Warning: Each child in a nested array or iterator should have a ' +
138
+ 'unique "key" prop. Check the React.render call using <div>. See ' +
139
'https://fb.me/react-warning-keys for more information.'
140
);
141
});