diff --git a/src/isomorphic/children/ReactChildren.js b/src/isomorphic/children/ReactChildren.js index 1e275b601c..da8848d229 100644 --- a/src/isomorphic/children/ReactChildren.js +++ b/src/isomorphic/children/ReactChildren.js @@ -117,8 +117,8 @@ function mapSingleChildIntoContext(bookKeeping, child, childKey) { // traverseAllChildren used to do for objects as children keyPrefix + ( - mappedChild !== child ? - escapeUserProvidedKey(mappedChild.key || '') + '/' : + (mappedChild.key && (!child || (child.key !== mappedChild.key))) ? + escapeUserProvidedKey(mappedChild.key) + '/' : '' ) + childKey diff --git a/src/isomorphic/children/__tests__/ReactChildren-test.js b/src/isomorphic/children/__tests__/ReactChildren-test.js index a85d9dbf0e..e638c68c10 100644 --- a/src/isomorphic/children/__tests__/ReactChildren-test.js +++ b/src/isomorphic/children/__tests__/ReactChildren-test.js @@ -85,7 +85,7 @@ describe('ReactChildren', function() { expect(ReactChildren.count(mappedChildren)).toBe(1); expect(mappedChildren[0]).not.toBe(simpleKid); expect(mappedChildren[0].props.children).toBe(simpleKid); - expect(mappedChildren[0].key).toBe('/.$simple'); + expect(mappedChildren[0].key).toBe('.$simple'); }); it('should invoke callback with the right context', function() { @@ -117,17 +117,15 @@ describe('ReactChildren', function() { var three = null; var four =
; - var zeroMapped =
; // Key should be joined to obj key - var oneMapped = null; // Key should be added even if we don't supply it! - var twoMapped =
; // Key should be added even if not supplied! - var threeMapped = ; // Map from null to something. - var fourMapped =
; - + var mapped = [ +
, // Key should be joined to obj key + null, // Key should be added even if we don't supply it! +
, // Key should be added even if not supplied! + , // Map from null to something. +
, + ]; var callback = jasmine.createSpy().andCallFake(function(kid, index) { - return index === 0 ? zeroMapped : - index === 1 ? oneMapped : - index === 2 ? twoMapped : - index === 3 ? threeMapped : fourMapped; + return mapped[index]; }); var instance = ( @@ -159,7 +157,7 @@ describe('ReactChildren', function() { mappedChildren[2].key, mappedChildren[3].key, ]).toEqual( - ['giraffe/.$keyZero', '/.$keyTwo', '/.3', 'keyFour/.$keyFour'] + ['giraffe/.$keyZero', '.$keyTwo', '.3', '.$keyFour'] ); expect(callback).toHaveBeenCalledWith(zero, 0); @@ -169,9 +167,9 @@ describe('ReactChildren', function() { expect(callback).toHaveBeenCalledWith(four, 4); expect(mappedChildren[0]).toEqual(
); - expect(mappedChildren[1]).toEqual(
); - expect(mappedChildren[2]).toEqual(); - expect(mappedChildren[3]).toEqual(
); + expect(mappedChildren[1]).toEqual(
); + expect(mappedChildren[2]).toEqual(); + expect(mappedChildren[3]).toEqual(
); }); it('should be called for each child in nested structure', function() { @@ -241,15 +239,15 @@ describe('ReactChildren', function() { mappedChildren[3].key, ]).toEqual([ 'giraffe/.0:$firstHalfKey/.$keyZero', - '/.0:$firstHalfKey/.$keyTwo', + '.0:$firstHalfKey/.$keyTwo', 'keyFour/.0:$secondHalfKey/.$keyFour', - '/.0:$keyFive/.$keyFiveInner', + '.0:$keyFive/.$keyFiveInner', ]); expect(mappedChildren[0]).toEqual(
); - expect(mappedChildren[1]).toEqual(
); + expect(mappedChildren[1]).toEqual(
); expect(mappedChildren[2]).toEqual(
); - expect(mappedChildren[3]).toEqual(
); + expect(mappedChildren[3]).toEqual(
); }); it('should retain key across two mappings', function() { @@ -272,7 +270,7 @@ describe('ReactChildren', function() {
); - var expectedForcedKeys = ['giraffe/.$keyZero', '/.$keyOne']; + var expectedForcedKeys = ['giraffe/.$keyZero', '.$keyOne']; var mappedChildrenForcedKeys = ReactChildren.map(forcedKeys.props.children, mapFn); var mappedForcedKeys = mappedChildrenForcedKeys.map((c) => c.key); @@ -280,7 +278,7 @@ describe('ReactChildren', function() { var expectedRemappedForcedKeys = [ 'giraffe/.$giraffe/.$keyZero', - '/.$/.$keyOne', + '.$.$keyOne', ]; var remappedChildrenForcedKeys = ReactChildren.map(mappedChildrenForcedKeys, mapFn); @@ -310,6 +308,46 @@ describe('ReactChildren', function() { }).not.toThrow(); }); + it('should use the same key for a cloned element', function() { + var instance = ( +
+
+
+ ); + + var mapped = ReactChildren.map( + instance.props.children, + element => element, + ); + + var mappedWithClone = ReactChildren.map( + instance.props.children, + element => React.cloneElement(element), + ); + + expect(mapped[0].key).toBe(mappedWithClone[0].key); + }); + + it('should use the same key for a cloned element with key', function() { + var instance = ( +
+
+
+ ); + + var mapped = ReactChildren.map( + instance.props.children, + element => element, + ); + + var mappedWithClone = ReactChildren.map( + instance.props.children, + element => React.cloneElement(element, {key: 'unique'}), + ); + + expect(mapped[0].key).toBe(mappedWithClone[0].key); + }); + it('should return 0 for null children', function() { var numberOfChildren = ReactChildren.count(null); expect(numberOfChildren).toBe(0);