mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Revert "Add key warning to nested collections"
This heuristic isn't great because it relies on inspecting deep children which aren't guaranteed to be React elements. In particular, this was causing stack overflows in a component we had that used a *DOM node* as children, like `<DOMContainer>{node}</DOMContainer>`.
This reverts commits:
0a3aa8493a
64c9d9d762
0c58f4f6b1
8cf226e442
086636747f
This commit is contained in:
@@ -49,13 +49,13 @@ describe('ReactFragment', function() {
|
||||
z: <span />
|
||||
};
|
||||
var element = <div>{[children]}</div>;
|
||||
expect(console.error.calls.length).toBe(0);
|
||||
var container = document.createElement('div');
|
||||
React.render(element, container);
|
||||
expect(console.error.calls.length).toBe(1);
|
||||
expect(console.error.calls[0].args[0]).toContain(
|
||||
'Any use of a keyed object'
|
||||
);
|
||||
var container = document.createElement('div');
|
||||
React.render(element, container);
|
||||
expect(console.error.calls.length).toBe(1);
|
||||
});
|
||||
|
||||
it('should warn if accessing any property on a fragment', function() {
|
||||
|
||||
@@ -80,21 +80,6 @@ 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.
|
||||
@@ -121,6 +106,20 @@ var ReactElement = function(type, key, ref, owner, context, props) {
|
||||
// commonly used development environments.
|
||||
this._store = {props: props, originalProps: assign({}, props)};
|
||||
|
||||
// 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(this._store, 'validated', {
|
||||
configurable: false,
|
||||
enumerable: false,
|
||||
writable: true
|
||||
});
|
||||
} catch (x) {
|
||||
}
|
||||
this._store.validated = false;
|
||||
|
||||
// We're not allowed to set props directly on the object so we early
|
||||
// return and rely on the prototype membrane to forward to the backing
|
||||
// store.
|
||||
@@ -171,9 +170,6 @@ ReactElement.createElement = function(type, 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];
|
||||
}
|
||||
@@ -220,6 +216,11 @@ ReactElement.cloneAndReplaceProps = function(oldElement, newProps) {
|
||||
oldElement._context,
|
||||
newProps
|
||||
);
|
||||
|
||||
if (__DEV__) {
|
||||
// If the key on the original is valid, then the clone is valid
|
||||
newElement._store.validated = oldElement._store.validated;
|
||||
}
|
||||
return newElement;
|
||||
};
|
||||
|
||||
@@ -261,9 +262,6 @@ 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];
|
||||
}
|
||||
|
||||
@@ -90,19 +90,15 @@ 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, deep) {
|
||||
if (element.key != null) {
|
||||
function validateExplicitKey(element, parentType) {
|
||||
if (element._store.validated || element.key != null) {
|
||||
return;
|
||||
}
|
||||
element._store.validated = true;
|
||||
|
||||
warnAndMonitorForKeyUse(
|
||||
// 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.',
|
||||
'Each child in an array or iterator should have a unique "key" prop.',
|
||||
element,
|
||||
parentType
|
||||
);
|
||||
@@ -116,17 +112,13 @@ function validateExplicitKey(element, parentType, deep) {
|
||||
* @param {string} name Property name of the key.
|
||||
* @param {ReactElement} element Component that requires a key.
|
||||
* @param {*} parentType element's parent's type.
|
||||
* @param {boolean} deep false if top-level collection, true if nested
|
||||
*/
|
||||
function validatePropertyKey(name, element, parentType, deep) {
|
||||
function validatePropertyKey(name, element, parentType) {
|
||||
if (!NUMERIC_PROPERTY_REGEX.test(name)) {
|
||||
return;
|
||||
}
|
||||
warnAndMonitorForKeyUse(
|
||||
deep ?
|
||||
'Nested child objects should have non-numeric keys ' +
|
||||
'so ordering is preserved.' :
|
||||
'Child objects should have non-numeric keys so ordering is preserved.',
|
||||
'Child objects should have non-numeric keys so ordering is preserved.',
|
||||
element,
|
||||
parentType
|
||||
);
|
||||
@@ -188,29 +180,18 @@ 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, deep) {
|
||||
function validateChildKeys(node, parentType) {
|
||||
if (Array.isArray(node)) {
|
||||
if (node._reactChildKeysValidated) {
|
||||
// All child elements were passed in a valid location.
|
||||
return;
|
||||
}
|
||||
for (var i = 0; i < node.length; i++) {
|
||||
var child = node[i];
|
||||
if (ReactElement.isValidElement(child)) {
|
||||
validateExplicitKey(child, parentType, deep);
|
||||
} else {
|
||||
// TODO: Warn on unkeyed arrays and suggest using createFragment
|
||||
validateChildKeys(child, parentType, true);
|
||||
validateExplicitKey(child, parentType);
|
||||
}
|
||||
}
|
||||
} else if (
|
||||
typeof node === 'string' || typeof node === 'number' ||
|
||||
ReactElement.isValidElement(node)
|
||||
) {
|
||||
} else if (ReactElement.isValidElement(node)) {
|
||||
// This element was passed in a valid location.
|
||||
return;
|
||||
node._store.validated = true;
|
||||
} else if (node) {
|
||||
var iteratorFn = getIteratorFn(node);
|
||||
// Entry iterators provide implicit keys.
|
||||
@@ -220,9 +201,7 @@ function validateChildKeys(node, parentType, deep) {
|
||||
var step;
|
||||
while (!(step = iterator.next()).done) {
|
||||
if (ReactElement.isValidElement(step.value)) {
|
||||
validateExplicitKey(step.value, parentType, deep);
|
||||
} else {
|
||||
validateChildKeys(step.value, parentType, true);
|
||||
validateExplicitKey(step.value, parentType);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -230,8 +209,7 @@ function validateChildKeys(node, parentType, deep) {
|
||||
var fragment = ReactFragment.extractIfFragment(node);
|
||||
for (var key in fragment) {
|
||||
if (fragment.hasOwnProperty(key)) {
|
||||
validatePropertyKey(key, fragment[key], parentType, deep);
|
||||
validateChildKeys(fragment[key], parentType, true);
|
||||
validatePropertyKey(key, fragment[key], parentType);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -437,7 +415,7 @@ var ReactElementValidator = {
|
||||
}
|
||||
|
||||
for (var i = 2; i < arguments.length; i++) {
|
||||
validateChildKeys(arguments[i], type, false);
|
||||
validateChildKeys(arguments[i], type);
|
||||
}
|
||||
|
||||
validatePropTypes(element);
|
||||
@@ -485,7 +463,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, false);
|
||||
validateChildKeys(arguments[i], newElement.type);
|
||||
}
|
||||
validatePropTypes(newElement);
|
||||
return newElement;
|
||||
|
||||
@@ -120,56 +120,6 @@ describe('ReactElementValidator', function() {
|
||||
);
|
||||
});
|
||||
|
||||
it('warns for keys for nested arrays of elements', function() {
|
||||
spyOn(console, 'error');
|
||||
|
||||
var divs = [
|
||||
[
|
||||
<div />,
|
||||
<div />
|
||||
],
|
||||
<div key="foo" />
|
||||
];
|
||||
ReactTestUtils.renderIntoDocument(<div>{divs}</div>);
|
||||
|
||||
expect(console.error.argsForCall.length).toBe(1);
|
||||
expect(console.error.argsForCall[0][0]).toBe(
|
||||
'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.'
|
||||
);
|
||||
});
|
||||
|
||||
it('warns for keys when reusing children', function() {
|
||||
spyOn(console, 'error');
|
||||
|
||||
var f = <span />;
|
||||
var g = <span />;
|
||||
|
||||
var children = [f, g];
|
||||
|
||||
ReactTestUtils.renderIntoDocument(
|
||||
<div>
|
||||
<div key="0">
|
||||
{g}
|
||||
</div>
|
||||
<div key="1">
|
||||
{f}
|
||||
</div>
|
||||
<div key="2">
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
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 ' +
|
||||
'https://fb.me/react-warning-keys for more information.'
|
||||
);
|
||||
});
|
||||
|
||||
it('does not warn for keys when passing children down', function() {
|
||||
spyOn(console, 'error');
|
||||
|
||||
|
||||
Reference in New Issue
Block a user