Ignore children with clashing keys
Fixes #566.
Ben Alpert committed
Apr 6, 2014 at 20:47 UTC
216fcdeb42cd4d783a89f618ed989010c10506fc
3 files changed
+29
-20
src/utils/ReactChildren.js
+13
-8
@@ -20,8 +20,8 @@
20
21
var PooledClass = require('PooledClass');
22
23
-var invariant = require('invariant');
23
var traverseAllChildren = require('traverseAllChildren');
24
+var warning = require('warning');
25
26
var twoArgumentPooler = PooledClass.twoArgumentPooler;
27
var threeArgumentPooler = PooledClass.threeArgumentPooler;
@@ -86,16 +86,21 @@ PooledClass.addPoolingTo(MapBookKeeping, threeArgumentPooler);
86
function mapSingleChildIntoContext(traverseContext, child, name, i) {
87
var mapBookKeeping = traverseContext;
88
var mapResult = mapBookKeeping.mapResult;
89
- var mappedChild =
90
- mapBookKeeping.mapFunction.call(mapBookKeeping.mapContext, child, i);
91
- // We found a component instance
92
- invariant(
93
- !mapResult.hasOwnProperty(name),
89
+
90
+ var keyUnique = !mapResult.hasOwnProperty(name);
91
+ warning(
92
+ keyUnique,
93
'ReactChildren.map(...): Encountered two children with the same key, ' +
95
- '`%s`. Children keys must be unique.',
94
+ '`%s`. Child keys must be unique; when two children share a key, only ' +
95
+ 'the first child will be used.',
96
name
97
);
98
- mapResult[name] = mappedChild;
98
+
99
+ if (keyUnique) {
100
+ var mappedChild =
101
+ mapBookKeeping.mapFunction.call(mapBookKeeping.mapContext, child, i);
102
+ mapResult[name] = mappedChild;
103
+ }
104
}
105
106
/**
src/utils/__tests__/ReactChildren-test.js
+8
-6
@@ -320,17 +320,19 @@ describe('ReactChildren', function() {
320
}).not.toThrow();
321
});
322
323
- it('should throw if key provided is a dupe with explicit key', function() {
323
+ it('should warn if key provided is a dupe with explicit key', function() {
324
var zero = <div key="something"/>;
325
- var one = <div key="something" />;
325
+ var one = <span key="something" />;
326
327
- var mapFn = function() {return null;};
327
+ var mapFn = function(component) { return component; };
328
var instance = (
329
<div>{zero}{one}</div>
330
);
331
332
- expect(function() {
333
- ReactChildren.map(instance.props.children, mapFn);
334
- }).toThrow();
332
+ spyOn(console, 'warn');
333
+ var mapped = ReactChildren.map(instance.props.children, mapFn);
334
+
335
+ expect(console.warn.calls.length).toEqual(1);
336
+ expect(mapped).toEqual({'.$something': zero});
337
});
338
});
src/utils/flattenChildren.js
+8
-6
@@ -18,8 +18,8 @@
18
19
"use strict";
20
21
-var invariant = require('invariant');
21
var traverseAllChildren = require('traverseAllChildren');
22
+var warning = require('warning');
23
24
/**
25
* @param {function} traverseContext Context passed through traversal.
@@ -29,13 +29,15 @@ var traverseAllChildren = require('traverseAllChildren');
29
function flattenSingleChildIntoContext(traverseContext, child, name) {
30
// We found a component instance.
31
var result = traverseContext;
32
- invariant(
33
- !result.hasOwnProperty(name),
34
- 'flattenChildren(...): Encountered two children with the same key, `%s`. ' +
35
- 'Children keys must be unique.',
32
+ var keyUnique = !result.hasOwnProperty(name);
33
+ warning(
34
+ keyUnique,
35
+ 'flattenChildren(...): Encountered two children with the same key, ' +
36
+ '`%s`. Child keys must be unique; when two children share a key, only ' +
37
+ 'the first child will be used.',
38
name
39
);
38
- if (child != null) {
40
+ if (keyUnique && child != null) {
41
result[name] = child;
42
}
43
}