diff --git a/src/addons/transitions/__tests__/ReactTransitionKeySet-test.js b/src/addons/transitions/__tests__/ReactTransitionKeySet-test.js
index 993ebbc6e09..8e3954a5f3a 100644
--- a/src/addons/transitions/__tests__/ReactTransitionKeySet-test.js
+++ b/src/addons/transitions/__tests__/ReactTransitionKeySet-test.js
@@ -36,8 +36,8 @@ describe('ReactTransitionKeySet', function() {
var component =
{one}{two}
;
expect(ReactTransitionKeySet.getChildMapping(component.props.children))
.toEqual({
- '{one}': one,
- '{two}': two
+ '.$one': one,
+ '.$two': two
});
});
@@ -48,8 +48,8 @@ describe('ReactTransitionKeySet', function() {
var two = ;
var component = {one}{two}
;
expect(ReactTransitionKeySet.getKeySet(component.props.children)).toEqual({
- '{one}': true,
- '{two}': true
+ '.$one': true,
+ '.$two': true
});
});
diff --git a/src/core/ReactInstanceHandles.js b/src/core/ReactInstanceHandles.js
index 1a78bc6d9e9..b64b303ea46 100644
--- a/src/core/ReactInstanceHandles.js
+++ b/src/core/ReactInstanceHandles.js
@@ -39,7 +39,7 @@ var MAX_TREE_DEPTH = 100;
* @internal
*/
function getReactRootIDString(index) {
- return SEPARATOR + 'r[' + index.toString(36) + ']';
+ return SEPARATOR + index.toString(36);
}
/**
@@ -241,7 +241,7 @@ var ReactInstanceHandles = {
* @internal
*/
createReactID: function(rootID, name) {
- return rootID + SEPARATOR + name;
+ return rootID + name;
},
/**
@@ -253,8 +253,11 @@ var ReactInstanceHandles = {
* @internal
*/
getReactRootIDFromNodeID: function(id) {
- var regexResult = /\.r\[[^\]]+\]/.exec(id);
- return regexResult && regexResult[0];
+ if (id && id.charAt(0) === SEPARATOR && id.length > 1) {
+ var index = id.indexOf(SEPARATOR, 1);
+ return index > -1 ? id.substr(0, index) : id;
+ }
+ return null;
},
/**
diff --git a/src/core/ReactMultiChild.js b/src/core/ReactMultiChild.js
index dd9ce4e2e0b..58711eeae68 100644
--- a/src/core/ReactMultiChild.js
+++ b/src/core/ReactMultiChild.js
@@ -193,7 +193,7 @@ var ReactMultiChild = {
var child = children[name];
if (children.hasOwnProperty(name)) {
// Inlined for performance, see `ReactInstanceHandles.createReactID`.
- var rootID = this._rootNodeID + '.' + name;
+ var rootID = this._rootNodeID + name;
var mountImage = child.mountComponent(
rootID,
transaction,
@@ -385,7 +385,7 @@ var ReactMultiChild = {
*/
_mountChildByNameAtIndex: function(child, name, index, transaction) {
// Inlined for performance, see `ReactInstanceHandles.createReactID`.
- var rootID = this._rootNodeID + '.' + name;
+ var rootID = this._rootNodeID + name;
var mountImage = child.mountComponent(
rootID,
transaction,
diff --git a/src/core/__tests__/ReactIdentity-test.js b/src/core/__tests__/ReactIdentity-test.js
index 3e6012b229d..c77964ef159 100644
--- a/src/core/__tests__/ReactIdentity-test.js
+++ b/src/core/__tests__/ReactIdentity-test.js
@@ -34,7 +34,7 @@ describe('ReactIdentity', function() {
ReactMount = require('ReactMount');
});
- var idExp = /^\.r\[.+?\](.*)$/;
+ var idExp = /^\.[^.]+(.*)$/;
function checkId(child, expectedId) {
var actual = idExp.exec(ReactMount.getID(child));
var expected = idExp.exec(expectedId);
@@ -55,8 +55,8 @@ describe('ReactIdentity', function() {
React.renderComponent(instance, document.createElement('div'));
var node = instance.getDOMNode();
reactComponentExpect(instance).toBeDOMComponentWithChildCount(2);
- checkId(node.childNodes[0], '.r[0].{first}[0]');
- checkId(node.childNodes[1], '.r[0].{second}[0]');
+ checkId(node.childNodes[0], '.0.$first:0');
+ checkId(node.childNodes[1], '.0.$second:0');
});
it('should allow key property to express identity', function() {
@@ -71,10 +71,10 @@ describe('ReactIdentity', function() {
React.renderComponent(instance, document.createElement('div'));
var node = instance.getDOMNode();
reactComponentExpect(instance).toBeDOMComponentWithChildCount(4);
- checkId(node.childNodes[0], '.r[0].{apple}');
- checkId(node.childNodes[1], '.r[0].{banana}');
- checkId(node.childNodes[2], '.r[0].{0}');
- checkId(node.childNodes[3], '.r[0].{123}');
+ checkId(node.childNodes[0], '.0.$apple');
+ checkId(node.childNodes[1], '.0.$banana');
+ checkId(node.childNodes[2], '.0.$0');
+ checkId(node.childNodes[3], '.0.$123');
});
it('should use instance identity', function() {
@@ -96,12 +96,12 @@ describe('ReactIdentity', function() {
var node = instance.getDOMNode();
reactComponentExpect(instance).toBeDOMComponentWithChildCount(3);
- checkId(node.childNodes[0], '.r[0].{wrap1}');
- checkId(node.childNodes[0].firstChild, '.r[0].{wrap1}.{squirrel}');
- checkId(node.childNodes[1], '.r[0].{wrap2}');
- checkId(node.childNodes[1].firstChild, '.r[0].{wrap2}.{bunny}');
- checkId(node.childNodes[2], '.r[0].[2]');
- checkId(node.childNodes[2].firstChild, '.r[0].[2].{chipmunk}');
+ checkId(node.childNodes[0], '.0.$wrap1');
+ checkId(node.childNodes[0].firstChild, '.0.$wrap1.$squirrel');
+ checkId(node.childNodes[1], '.0.$wrap2');
+ checkId(node.childNodes[1].firstChild, '.0.$wrap2.$bunny');
+ checkId(node.childNodes[2], '.0.2');
+ checkId(node.childNodes[2].firstChild, '.0.2.$chipmunk');
});
function renderAComponentWithKeyIntoContainer(key, container) {
@@ -116,8 +116,10 @@ describe('ReactIdentity', function() {
expect(span1.getDOMNode()).not.toBe(null);
expect(span2.getDOMNode()).not.toBe(null);
- checkId(span1.getDOMNode(), '.r[0].{' + key + '}');
- checkId(span2.getDOMNode(), '.r[0].[1]{' + key + '}[0]');
+ key = key.replace(/=/g, '=0');
+
+ checkId(span1.getDOMNode(), '.0.$' + key);
+ checkId(span2.getDOMNode(), '.0.1:$' + key + ':0');
}
it('should allow any character as a key, in a detached parent', function() {
diff --git a/src/core/__tests__/ReactInstanceHandles-test.js b/src/core/__tests__/ReactInstanceHandles-test.js
index 5f2048f6843..b33502aa804 100644
--- a/src/core/__tests__/ReactInstanceHandles-test.js
+++ b/src/core/__tests__/ReactInstanceHandles-test.js
@@ -154,13 +154,25 @@ describe('ReactInstanceHandles', function() {
describe('getReactRootIDFromNodeID', function() {
it('should support strings', function() {
- var test = '.r[s_0_1][0]..[1]';
- var expected = '.r[s_0_1]';
+ var test = '.s_0_1.0..1';
+ var expected = '.s_0_1';
var actual = ReactInstanceHandles.getReactRootIDFromNodeID(test);
expect(actual).toEqual(expected);
});
});
+ describe('getReactRootIDFromNodeID', function() {
+ it('should return null for invalid IDs', function() {
+ var getReactRootIDFromNodeID = (
+ ReactInstanceHandles.getReactRootIDFromNodeID
+ );
+
+ expect(getReactRootIDFromNodeID(null)).toEqual(null);
+ expect(getReactRootIDFromNodeID('.')).toEqual(null);
+ expect(getReactRootIDFromNodeID('#')).toEqual(null);
+ });
+ });
+
describe('traverseTwoPhase', function() {
it("should not traverse when traversing outside DOM", function() {
var targetID = '';
diff --git a/src/core/__tests__/ReactMultiChildReconcile-test.js b/src/core/__tests__/ReactMultiChildReconcile-test.js
index 0296999223a..fb28eeb60d3 100644
--- a/src/core/__tests__/ReactMultiChildReconcile-test.js
+++ b/src/core/__tests__/ReactMultiChildReconcile-test.js
@@ -46,7 +46,9 @@ var stripEmptyValues = function(obj) {
* here. This relies on an implementation detail of the rendering system.
*/
var getOriginalKey = function(childName) {
- return childName.slice('{'.length, childName.length - '}[0]'.length);
+ var match = childName.match(/^\.\$([^.]+)\:0$/);
+ expect(match).not.toBeNull();
+ return match[1];
};
/**
diff --git a/src/utils/__tests__/ReactChildren-test.js b/src/utils/__tests__/ReactChildren-test.js
index ebbfe51d039..7424bb9eb36 100644
--- a/src/utils/__tests__/ReactChildren-test.js
+++ b/src/utils/__tests__/ReactChildren-test.js
@@ -92,7 +92,7 @@ describe('ReactChildren', function() {
expect(mappedKeys.length).toBe(1);
expect(mappedChildren[mappedKeys[0]]).not.toBe(simpleKid);
expect(mappedChildren[mappedKeys[0]].props.children).toBe(simpleKid);
- expect(mappedKeys[0]).toBe('{simple}');
+ expect(mappedKeys[0]).toBe('.$simple');
});
it('should invoke callback with the right context', function() {
@@ -162,7 +162,7 @@ describe('ReactChildren', function() {
expect(mappedKeys.length).toBe(5);
// Keys default to indices.
expect(mappedKeys).toEqual(
- ['{keyZero}', '[1]', '{keyTwo}', '[3]', '{keyFour}']
+ ['.$keyZero', '.1', '.$keyTwo', '.3', '.$keyFour']
);
expect(callback).toHaveBeenCalledWith(zero, 0);
@@ -235,12 +235,12 @@ describe('ReactChildren', function() {
expect(mappedKeys.length).toBe(6);
// Keys default to indices.
expect(mappedKeys).toEqual([
- '[0]{firstHalfKey}[0]{keyZero}',
- '[0]{firstHalfKey}[0][1]',
- '[0]{firstHalfKey}[0]{keyTwo}',
- '[0]{secondHalfKey}[0][0]',
- '[0]{secondHalfKey}[0]{keyFour}',
- '[0]{keyFive}{keyFiveInner}'
+ '.0:$firstHalfKey:0:$keyZero',
+ '.0:$firstHalfKey:0:1',
+ '.0:$firstHalfKey:0:$keyTwo',
+ '.0:$secondHalfKey:0:0',
+ '.0:$secondHalfKey:0:$keyFour',
+ '.0:$keyFive:$keyFiveInner'
]);
expect(callback).toHaveBeenCalledWith(zero, 0);
@@ -282,15 +282,15 @@ describe('ReactChildren', function() {
);
- var expectedForcedKeys = ['{keyZero}', '{keyOne}'];
+ var expectedForcedKeys = ['.$keyZero', '.$keyOne'];
var mappedChildrenForcedKeys =
ReactChildren.map(forcedKeys.props.children, mapFn);
var mappedForcedKeys = Object.keys(mappedChildrenForcedKeys);
expect(mappedForcedKeys).toEqual(expectedForcedKeys);
var expectedRemappedForcedKeys = [
- '{{keyZero^C}{giraffe}',
- '{{keyOne^C}[0]'
+ '.$=1$keyZero:$giraffe',
+ '.$=1$keyOne:0'
];
var remappedChildrenForcedKeys =
ReactChildren.map(mappedChildrenForcedKeys, mapFn);
diff --git a/src/utils/__tests__/sliceChildren-test.js b/src/utils/__tests__/sliceChildren-test.js
index 1b3bfb26ecd..cac790463f1 100644
--- a/src/utils/__tests__/sliceChildren-test.js
+++ b/src/utils/__tests__/sliceChildren-test.js
@@ -68,9 +68,9 @@ describe('sliceChildren', function() {
];
var children = renderAndSlice(fullSet, 0);
expect(children).toEqual({
- '{A}': fullSet[0],
- '{B}': fullSet[1],
- '{C}': fullSet[2]
+ '.$A': fullSet[0],
+ '.$B': fullSet[1],
+ '.$C': fullSet[2]
});
});
@@ -82,8 +82,8 @@ describe('sliceChildren', function() {
];
var children = renderAndSlice(fullSet, 1);
expect(children).toEqual({
- '{B}': fullSet[1],
- '{C}': fullSet[2]
+ '.$B': fullSet[1],
+ '.$C': fullSet[2]
});
});
@@ -96,7 +96,7 @@ describe('sliceChildren', function() {
];
var children = renderAndSlice(fullSet, 1, 2);
expect(children).toEqual({
- '{B}': fullSet[1]
+ '.$B': fullSet[1]
});
});
@@ -112,7 +112,7 @@ describe('sliceChildren', function() {
.instance();
expect(rendered.props.children).toEqual({
- '[1]': b
+ '.1': b
});
});
diff --git a/src/utils/__tests__/traverseAllChildren-test.js b/src/utils/__tests__/traverseAllChildren-test.js
index 7386a5d6acd..0b66e04d458 100644
--- a/src/utils/__tests__/traverseAllChildren-test.js
+++ b/src/utils/__tests__/traverseAllChildren-test.js
@@ -43,7 +43,7 @@ describe('traverseAllChildren', function() {
expect(traverseFn).toHaveBeenCalledWith(
traverseContext,
simpleKid,
- '{simple}',
+ '.$simple',
0
);
expect(traverseContext.length).toEqual(1);
@@ -62,7 +62,7 @@ describe('traverseAllChildren', function() {
expect(traverseFn).toHaveBeenCalledWith(
traverseContext,
simpleKid,
- '[0]',
+ '.0',
0
);
expect(traverseContext.length).toEqual(1);
@@ -81,7 +81,7 @@ describe('traverseAllChildren', function() {
expect(traverseFn).toHaveBeenCalledWith(
traverseContext,
simpleKid,
- '[0]',
+ '.0',
0
);
expect(traverseContext.length).toEqual(1);
@@ -114,21 +114,21 @@ describe('traverseAllChildren', function() {
expect(traverseFn).toHaveBeenCalledWith(
traverseContext,
zero,
- '{keyZero}',
+ '.$keyZero',
0
);
- expect(traverseFn).toHaveBeenCalledWith(traverseContext, one, '[1]', 1);
+ expect(traverseFn).toHaveBeenCalledWith(traverseContext, one, '.1', 1);
expect(traverseFn).toHaveBeenCalledWith(
traverseContext,
two,
- '{keyTwo}',
+ '.$keyTwo',
2
);
- expect(traverseFn).toHaveBeenCalledWith(traverseContext, three, '[3]', 3);
+ expect(traverseFn).toHaveBeenCalledWith(traverseContext, three, '.3', 3);
expect(traverseFn).toHaveBeenCalledWith(
traverseContext,
four,
- '{keyFour}',
+ '.$keyFour',
4
);
});
@@ -171,38 +171,38 @@ describe('traverseAllChildren', function() {
expect(traverseFn).toHaveBeenCalledWith(
traverseContext,
zero,
- '[0]{firstHalfKey}[0]{keyZero}',
+ '.0:$firstHalfKey:0:$keyZero',
0
);
expect(traverseFn)
- .toHaveBeenCalledWith(traverseContext, one, '[0]{firstHalfKey}[0][1]', 1);
+ .toHaveBeenCalledWith(traverseContext, one, '.0:$firstHalfKey:0:1', 1);
expect(traverseFn).toHaveBeenCalledWith(
traverseContext,
two,
- '[0]{firstHalfKey}[0]{keyTwo}',
+ '.0:$firstHalfKey:0:$keyTwo',
2
);
expect(traverseFn).toHaveBeenCalledWith(
traverseContext,
three,
- '[0]{secondHalfKey}[0][0]',
+ '.0:$secondHalfKey:0:0',
3
);
expect(traverseFn).toHaveBeenCalledWith(
traverseContext,
four,
- '[0]{secondHalfKey}[0]{keyFour}',
+ '.0:$secondHalfKey:0:$keyFour',
4
);
expect(traverseFn).toHaveBeenCalledWith(
traverseContext,
five,
- '[0]{keyFive}{keyFiveInner}',
+ '.0:$keyFive:$keyFiveInner',
5
);
});
@@ -228,13 +228,13 @@ describe('traverseAllChildren', function() {
expect(traverseFn).toHaveBeenCalledWith(
traverseContext,
zeroForceKey,
- '{keyZero}',
+ '.$keyZero',
0
);
expect(traverseFn).toHaveBeenCalledWith(
traverseContext,
oneForceKey,
- '{keyOne}',
+ '.$keyOne',
1
);
});
diff --git a/src/utils/traverseAllChildren.js b/src/utils/traverseAllChildren.js
index 0406ad268cb..8b935019e83 100644
--- a/src/utils/traverseAllChildren.js
+++ b/src/utils/traverseAllChildren.js
@@ -18,10 +18,14 @@
"use strict";
+var ReactInstanceHandles = require('ReactInstanceHandles');
var ReactTextComponent = require('ReactTextComponent');
var invariant = require('invariant');
+var SEPARATOR = ReactInstanceHandles.SEPARATOR;
+var SUBSEPARATOR = ':';
+
/**
* TODO: Test that:
* 1. `mapChildren` transforms strings and numbers into `ReactTextComponent`.
@@ -31,12 +35,12 @@ var invariant = require('invariant');
*/
var userProvidedKeyEscaperLookup = {
- '^': '^X',
- '.': '^D',
- '}': '^C'
+ '=': '=0',
+ '.': '=1',
+ ':': '=2'
};
-var userProvidedKeyEscapeRegex = /[.^}]/g;
+var userProvidedKeyEscapeRegex = /[=.:]/g;
function userProvidedKeyEscaper(match) {
return userProvidedKeyEscaperLookup[match];
@@ -55,7 +59,7 @@ function getComponentKey(component, index) {
return wrapUserProvidedKey(component.props.key);
}
// Implicit key determined by the index in the set
- return '[' + index + ']';
+ return index.toString(36);
}
/**
@@ -79,7 +83,7 @@ function escapeUserProvidedKey(text) {
* @return {string}
*/
function wrapUserProvidedKey(key) {
- return '{' + escapeUserProvidedKey(key) + '}';
+ return '$' + escapeUserProvidedKey(key);
}
/**
@@ -97,7 +101,11 @@ var traverseAllChildrenImpl =
if (Array.isArray(children)) {
for (var i = 0; i < children.length; i++) {
var child = children[i];
- var nextName = nameSoFar + getComponentKey(child, i);
+ var nextName = (
+ nameSoFar +
+ (nameSoFar ? SUBSEPARATOR : SEPARATOR) +
+ getComponentKey(child, i)
+ );
var nextIndex = indexSoFar + subtreeCount;
subtreeCount += traverseAllChildrenImpl(
child,
@@ -112,8 +120,9 @@ var traverseAllChildrenImpl =
var isOnlyChild = nameSoFar === '';
// If it's the only child, treat the name as if it was wrapped in an array
// so that it's consistent if the number of children grows
- var storageName = isOnlyChild ? getComponentKey(children, 0) : nameSoFar;
- if (children === null || children === undefined || type === 'boolean') {
+ var storageName =
+ isOnlyChild ? SEPARATOR + getComponentKey(children, 0) : nameSoFar;
+ if (children == null || type === 'boolean') {
// All of the above are perceived as null.
callback(traverseContext, null, storageName, indexSoFar);
subtreeCount = 1;
@@ -132,8 +141,8 @@ var traverseAllChildrenImpl =
subtreeCount += traverseAllChildrenImpl(
children[key],
(
- nameSoFar +
- wrapUserProvidedKey(key) +
+ nameSoFar + (nameSoFar ? SUBSEPARATOR : SEPARATOR) +
+ wrapUserProvidedKey(key) + SUBSEPARATOR +
getComponentKey(children[key], 0)
),
indexSoFar + subtreeCount,