The library for web and native user interfaces.
Better warnings for missing keys on arrays
We have less dynamic arrays in the code base now so let's start warning for all the cases where we pass dynamic arrays without keys. I use the displayName to point out which component's render method was responsible. I only warn once per component. If the child was created in a different component (and passed as a property) I also show the owner of the child. Maybe it should've attached the key at a higher level. This does give false positives for arrays that are truly static. Those should probably be refactored to use the XML syntax if possible.
CommitSyncScript committed
Jun 18, 2013 at 09:31 UTC
0e91febb9ce8eb6eac3154a6e4b06cfcc16115ab
1 file changed
+27
-6
src/core/ReactComponent.js
+27
-6
@@ -58,10 +58,12 @@ var ComponentLifeCycle = keyMirror({
58
*/
59
60
var CHILD_HAS_NO_IDENTITY =
61
- 'You are passing a dynamic array of children. You should set the ' +
62
- 'property "key" to a string that uniquely identifies each child.';
61
+ 'Each child in an array should have a unique "key" prop. ' +
62
+ 'Check the render method of ';
63
64
-var HAS_WARNED = false;
64
+var CHILD_CAME_FROM_ANOTHER_OWNER = '. It was passed a child from ';
65
+
66
+var owner_has_warned = {};
67
68
/**
69
* Helpers for flattening child arguments onto a new array or use an existing
@@ -108,9 +110,28 @@ function assignKey(groupingIndex, child, index) {
110
// This is in an array. This could grow and shrink or be reordered.
111
// All children that doesn't already have autogenerated identity needs
112
// to have an explicit key provided.
111
- if (!HAS_WARNED && key == null) {
112
- HAS_WARNED = true;
113
- console && console.warn && console.warn(CHILD_HAS_NO_IDENTITY);
113
+ if (key == null) {
114
+ // Name of the component whose render method tried to pass children.
115
+ var currentName =
116
+ ReactCurrentOwner.current &&
117
+ ReactCurrentOwner.current.constructor.displayName;
118
+
119
+ // Name of the component that originally created this child.
120
+ var childOwnerName =
121
+ child.props[OWNER] && child.props[OWNER].constructor.displayName;
122
+
123
+ if (currentName && !owner_has_warned.hasOwnProperty(currentName)) {
124
+ owner_has_warned[currentName] = true;
125
+ var message = CHILD_HAS_NO_IDENTITY + currentName;
126
+ if (childOwnerName && currentName !== childOwnerName) {
127
+ // Usually the current owner is the offender, but if it accepts
128
+ // children as a property, it may be the creator of the child that's
129
+ // responsible for assigning it a key.
130
+ message += CHILD_CAME_FROM_ANOTHER_OWNER + childOwnerName;
131
+ }
132
+ message += '.';
133
+ console && console.warn && console.warn(message);
134
+ }
135
}
136
}
137
child._key = createKey(key, index, groupingIndex);