diff --git a/src/core/ReactComponent.js b/src/core/ReactComponent.js index 67b4e90207..530a6f9c66 100644 --- a/src/core/ReactComponent.js +++ b/src/core/ReactComponent.js @@ -58,10 +58,12 @@ var ComponentLifeCycle = keyMirror({ */ var CHILD_HAS_NO_IDENTITY = - 'You are passing a dynamic array of children. You should set the ' + - 'property "key" to a string that uniquely identifies each child.'; + 'Each child in an array should have a unique "key" prop. ' + + 'Check the render method of '; -var HAS_WARNED = false; +var CHILD_CAME_FROM_ANOTHER_OWNER = '. It was passed a child from '; + +var owner_has_warned = {}; /** * Helpers for flattening child arguments onto a new array or use an existing @@ -108,9 +110,28 @@ function assignKey(groupingIndex, child, index) { // This is in an array. This could grow and shrink or be reordered. // All children that doesn't already have autogenerated identity needs // to have an explicit key provided. - if (!HAS_WARNED && key == null) { - HAS_WARNED = true; - console && console.warn && console.warn(CHILD_HAS_NO_IDENTITY); + if (key == null) { + // Name of the component whose render method tried to pass children. + var currentName = + ReactCurrentOwner.current && + ReactCurrentOwner.current.constructor.displayName; + + // Name of the component that originally created this child. + var childOwnerName = + child.props[OWNER] && child.props[OWNER].constructor.displayName; + + if (currentName && !owner_has_warned.hasOwnProperty(currentName)) { + owner_has_warned[currentName] = true; + var message = CHILD_HAS_NO_IDENTITY + currentName; + if (childOwnerName && currentName !== childOwnerName) { + // Usually the current owner is the offender, but if it accepts + // children as a property, it may be the creator of the child that's + // responsible for assigning it a key. + message += CHILD_CAME_FROM_ANOTHER_OWNER + childOwnerName; + } + message += '.'; + console && console.warn && console.warn(message); + } } } child._key = createKey(key, index, groupingIndex);