diff --git a/src/core/ReactComponent.js b/src/core/ReactComponent.js
index 42d4cb62c53a5..898df20c5659f 100644
--- a/src/core/ReactComponent.js
+++ b/src/core/ReactComponent.js
@@ -155,9 +155,21 @@ function validatePropertyKey(name) {
*/
function validateChildKeys(component) {
if (Array.isArray(component)) {
+ var keys = {};
for (var i = 0; i < component.length; i++) {
var child = component[i];
if (ReactComponent.isValidComponent(child)) {
+ if (child.props.key != null) {
+ if (keys[child.props.key]) {
+ var currentName = ReactCurrentOwner.current.constructor.displayName;
+ console.warn(
+ 'Key collision detected for key "' + child.props.key + '". ' +
+ 'Check the render method of ' + currentName + '.'
+ );
+ }
+
+ keys[child.props.key] = true;
+ }
validateExplicitKey(child);
}
}
diff --git a/src/core/__tests__/ReactComponent-test.js b/src/core/__tests__/ReactComponent-test.js
index 1899cfb7ccbaf..1035a09d9e63b 100644
--- a/src/core/__tests__/ReactComponent-test.js
+++ b/src/core/__tests__/ReactComponent-test.js
@@ -22,13 +22,23 @@
var React;
var ReactTestUtils;
+var mocks;
var reactComponentExpect;
+var warn;
describe('ReactComponent', function() {
beforeEach(function() {
React = require('React');
ReactTestUtils = require('ReactTestUtils');
reactComponentExpect = require('reactComponentExpect');
+ mocks = require('mocks');
+
+ warn = console.warn;
+ console.warn = mocks.getMockFunction();
+ });
+
+ afterEach(function() {
+ console.warn = warn;
});
it('should throw on invalid render targets', function() {
@@ -207,4 +217,30 @@ describe('ReactComponent', function() {
expect(root.refs.switcher.refs.box.refs.boxDiv._mountDepth).toBe(3);
expect(root.refs.child.refs.span._mountDepth).toBe(6);
});
+
+ it('should render even with key collisions', function() {
+ var Component = React.createClass({
+ render: function() {
+ var array = [
+ ,
+ ,
+ ,
+ ,
+
+ ];
+
+ return
{array}
;
+ }
+ });
+
+ var instance = ;
+ ReactTestUtils.renderIntoDocument(instance);
+
+ expect(console.warn.mock.calls.length).toBe(1);
+
+ expect(console.warn.mock.calls[0][0]).toBe(
+ 'Key collision detected for key "1". ' +
+ 'Check the render method of Component.'
+ );
+ });
});
diff --git a/src/utils/__tests__/ReactChildren-test.js b/src/utils/__tests__/ReactChildren-test.js
index b256d3fdf5fa0..544f78134c0e4 100644
--- a/src/utils/__tests__/ReactChildren-test.js
+++ b/src/utils/__tests__/ReactChildren-test.js
@@ -319,18 +319,4 @@ describe('ReactChildren', function() {
ReactChildren.map(instance.props.children, mapFn);
}).not.toThrow();
});
-
- it('should throw if key provided is a dupe with explicit key', function() {
- var zero = ;
- var one = ;
-
- var mapFn = function() {return null;};
- var instance = (
- {zero}{one}
- );
-
- expect(function() {
- ReactChildren.map(instance.props.children, mapFn);
- }).toThrow();
- });
});
diff --git a/src/utils/flattenChildren.js b/src/utils/flattenChildren.js
index 77650b67aa93a..f43f21a55d0fe 100644
--- a/src/utils/flattenChildren.js
+++ b/src/utils/flattenChildren.js
@@ -18,7 +18,6 @@
"use strict";
-var invariant = require('invariant');
var traverseAllChildren = require('traverseAllChildren');
/**
@@ -27,16 +26,8 @@ var traverseAllChildren = require('traverseAllChildren');
* @param {!string} name String name of key path to child.
*/
function flattenSingleChildIntoContext(traverseContext, child, name) {
- // We found a component instance.
- var result = traverseContext;
- invariant(
- !result.hasOwnProperty(name),
- 'flattenChildren(...): Encountered two children with the same key, `%s`. ' +
- 'Children keys must be unique.',
- name
- );
if (child != null) {
- result[name] = child;
+ traverseContext[name] = child;
}
}
diff --git a/src/utils/traverseAllChildren.js b/src/utils/traverseAllChildren.js
index 2fa6861f34bf1..e6969e428b5e5 100644
--- a/src/utils/traverseAllChildren.js
+++ b/src/utils/traverseAllChildren.js
@@ -98,13 +98,18 @@ function wrapUserProvidedKey(key) {
var traverseAllChildrenImpl =
function(children, nameSoFar, indexSoFar, callback, traverseContext) {
var subtreeCount = 0; // Count of children found in the current subtree.
+ var key;
if (Array.isArray(children)) {
for (var i = 0; i < children.length; i++) {
var child = children[i];
+ key = getComponentKey(child, i);
+ if (traverseContext && traverseContext.hasOwnProperty(key)) {
+ key = getComponentKey(null, i);
+ }
var nextName = (
nameSoFar +
(nameSoFar ? SUBSEPARATOR : SEPARATOR) +
- getComponentKey(child, i)
+ key
);
var nextIndex = indexSoFar + subtreeCount;
subtreeCount += traverseAllChildrenImpl(
@@ -136,7 +141,7 @@ var traverseAllChildrenImpl =
'traverseAllChildren(...): Encountered an invalid child; DOM ' +
'elements are not valid children of React components.'
);
- for (var key in children) {
+ for (key in children) {
if (children.hasOwnProperty(key)) {
subtreeCount += traverseAllChildrenImpl(
children[key],