Split out warning message for nested key warning

Also make sure to mark child array as validated in cloneElement as well.
This commit is contained in:
Ben Alpert 2015-05-12 16:33:04 -07:00
parent f0bdadf85b
commit 64c9d9d762
3 changed files with 38 additions and 27 deletions

View File

@ -80,6 +80,21 @@ function defineMutationMembrane(prototype) {
}
}
function markChildArrayValidated(childArray) {
// To make comparing ReactElements easier for testing purposes, we make the
// validation flag non-enumerable (where possible, which should include every
// environment we run tests in), so the test framework ignores it.
try {
Object.defineProperty(childArray, '_reactChildKeysValidated', {
configurable: false,
enumerable: false,
writable: true
});
} catch (x) {
}
childArray._reactChildKeysValidated = true;
}
/**
* Base constructor for all React elements. This is only used to make this
* work with a dynamic instanceof check. Nothing should live on this prototype.
@ -156,23 +171,9 @@ ReactElement.createElement = function(type, config, children) {
props.children = children;
} else if (childrenLength > 1) {
var childArray = Array(childrenLength);
if (__DEV__) {
// To make comparing ReactElements easier for testing purposes, we make
// the validation flag non-enumerable (where possible, which should
// include every environment we run tests in), so the test framework
// ignores it.
try {
Object.defineProperty(childArray, '_reactChildKeysValidated', {
configurable: false,
enumerable: false,
writable: true
});
} catch (x) {
}
childArray._reactChildKeysValidated = true;
markChildArrayValidated(childArray);
}
for (var i = 0; i < childrenLength; i++) {
childArray[i] = arguments[i + 2];
}
@ -260,6 +261,9 @@ ReactElement.cloneElement = function(element, config, children) {
props.children = children;
} else if (childrenLength > 1) {
var childArray = Array(childrenLength);
if (__DEV__) {
markChildArrayValidated(childArray);
}
for (var i = 0; i < childrenLength; i++) {
childArray[i] = arguments[i + 2];
}

View File

@ -90,13 +90,19 @@ function getCurrentOwnerDisplayName() {
* @internal
* @param {ReactElement} element Element that requires a key.
* @param {*} parentType element's parent's type.
* @param {boolean} deep false if top-level collection, true if nested
*/
function validateExplicitKey(element, parentType) {
function validateExplicitKey(element, parentType, deep) {
if (element.key != null) {
return;
}
warnAndMonitorForKeyUse(
'Each child in an array or iterator should have a unique "key" prop.',
// We vary the message for nested key warning to allow filtering them out
// since we didn't historically warn in this case.
deep ?
'Each child in a nested array or iterator should have ' +
'a unique "key" prop.' :
'Each child in an array or iterator should have a unique "key" prop.',
element,
parentType
);
@ -178,8 +184,9 @@ function warnAndMonitorForKeyUse(message, element, parentType) {
* @internal
* @param {ReactNode} node Statically passed child of any type.
* @param {*} parentType node's parent's type.
* @param {boolean} deep false if top-level collection, true if nested
*/
function validateChildKeys(node, parentType) {
function validateChildKeys(node, parentType, deep) {
if (Array.isArray(node)) {
if (node._reactChildKeysValidated) {
// All child elements were passed in a valid location.
@ -188,10 +195,10 @@ function validateChildKeys(node, parentType) {
for (var i = 0; i < node.length; i++) {
var child = node[i];
if (ReactElement.isValidElement(child)) {
validateExplicitKey(child, parentType);
validateExplicitKey(child, parentType, deep);
} else {
// TODO: Warn on unkeyed arrays and suggest using createFragment
validateChildKeys(child, parentType);
validateChildKeys(child, parentType, true);
}
}
} else if (
@ -209,9 +216,9 @@ function validateChildKeys(node, parentType) {
var step;
while (!(step = iterator.next()).done) {
if (ReactElement.isValidElement(step.value)) {
validateExplicitKey(step.value, parentType);
validateExplicitKey(step.value, parentType, deep);
} else {
validateChildKeys(step.value, parentType);
validateChildKeys(step.value, parentType, true);
}
}
}
@ -220,7 +227,7 @@ function validateChildKeys(node, parentType) {
for (var key in fragment) {
if (fragment.hasOwnProperty(key)) {
validatePropertyKey(key, fragment[key], parentType);
validateChildKeys(fragment[key], parentType);
validateChildKeys(fragment[key], parentType, true);
}
}
}
@ -426,7 +433,7 @@ var ReactElementValidator = {
}
for (var i = 2; i < arguments.length; i++) {
validateChildKeys(arguments[i], type);
validateChildKeys(arguments[i], type, false);
}
validatePropTypes(element);
@ -474,7 +481,7 @@ var ReactElementValidator = {
cloneElement: function(element, props, children) {
var newElement = ReactElement.cloneElement.apply(this, arguments);
for (var i = 2; i < arguments.length; i++) {
validateChildKeys(arguments[i], newElement.type);
validateChildKeys(arguments[i], newElement.type, false);
}
validatePropTypes(newElement);
return newElement;

View File

@ -134,8 +134,8 @@ describe('ReactElementValidator', function() {
expect(console.error.argsForCall.length).toBe(1);
expect(console.error.argsForCall[0][0]).toBe(
'Warning: Each child in an array or iterator should have a unique ' +
'"key" prop. Check the React.render call using <div>. See ' +
'Warning: Each child in a nested array or iterator should have a ' +
'unique "key" prop. Check the React.render call using <div>. See ' +
'https://fb.me/react-warning-keys for more information.'
);
});