Don't wrap text in <span> elements
Instead, use opening and closing comment nodes to delimit text data.
Michael Wiencek committed
Dec 30, 2015 at 02:01 UTC
203850013a42f1cde6fba1dea3c26c4b40e6cc90
11 files changed
+297
-55
src/renderers/dom/client/ReactDOMComponentTree.js
+6
-4
@@ -87,10 +87,12 @@ function precacheChildNodes(inst, node) {
87
}
88
// We assume the child nodes are in the same order as the child instances.
89
for (; childNode !== null; childNode = childNode.nextSibling) {
90
- if (childNode.nodeType === 1 &&
91
- childNode.getAttribute(ATTR_NAME) === String(childID) ||
92
- childNode.nodeType === 8 &&
93
- childNode.nodeValue === ' react-empty: ' + childID + ' ') {
90
+ if ((childNode.nodeType === 1 &&
91
+ childNode.getAttribute(ATTR_NAME) === String(childID)) ||
92
+ (childNode.nodeType === 8 &&
93
+ childNode.nodeValue === ' react-text: ' + childID + ' ') ||
94
+ (childNode.nodeType === 8 &&
95
+ childNode.nodeValue === ' react-empty: ' + childID + ' ')) {
96
precacheNode(childInst, childNode);
97
continue outer;
98
}
src/renderers/dom/client/__tests__/ReactDOMComponentTree-test.js
+3
-1
@@ -100,7 +100,9 @@ describe('ReactDOMComponentTree', function() {
100
expect(renderAndGetInstance('main')._currentElement.type).toBe('main');
101
102
// This one's a text component!
103
- expect(renderAndGetInstance('span')._stringText).toBe('goodbye.');
103
+ var root = renderAndQuery(null);
104
+ var inst = ReactDOMComponentTree.getInstanceFromNode(root.children[0].childNodes[2]);
105
+ expect(inst._stringText).toBe('goodbye.');
106
107
expect(renderAndGetClosest('b')._currentElement.type).toBe('main');
108
expect(renderAndGetClosest('img')._currentElement.type).toBe('main');
src/renderers/dom/client/utils/DOMChildrenOperations.js
+82
-2
@@ -22,6 +22,11 @@ var setInnerHTML = require('setInnerHTML');
22
var setTextContent = require('setTextContent');
23
24
function getNodeAfter(parentNode, node) {
25
+ // Special case for text components, which return [open, close] comments
26
+ // from getNativeNode.
27
+ if (Array.isArray(node)) {
28
+ node = node[1];
29
+ }
30
return node ? node.nextSibling : parentNode.firstChild;
31
}
32
@@ -51,6 +56,78 @@ function insertLazyTreeChildAt(parentNode, childTree, referenceNode) {
56
DOMLazyTree.insertTreeBefore(parentNode, childTree, referenceNode);
57
}
58
59
+function moveChild(parentNode, childNode, referenceNode) {
60
+ if (Array.isArray(childNode)) {
61
+ moveDelimitedText(parentNode, childNode[0], childNode[1], referenceNode);
62
+ } else {
63
+ insertChildAt(parentNode, childNode, referenceNode);
64
+ }
65
+}
66
+
67
+function removeChild(parentNode, childNode) {
68
+ if (Array.isArray(childNode)) {
69
+ var closingComment = childNode[1];
70
+ childNode = childNode[0];
71
+ removeDelimitedText(parentNode, childNode, closingComment);
72
+ parentNode.removeChild(closingComment);
73
+ }
74
+ parentNode.removeChild(childNode);
75
+}
76
+
77
+function moveDelimitedText(
78
+ parentNode,
79
+ openingComment,
80
+ closingComment,
81
+ referenceNode
82
+) {
83
+ var node = openingComment;
84
+ while (true) {
85
+ var nextNode = node.nextSibling;
86
+ insertChildAt(parentNode, node, referenceNode);
87
+ if (node === closingComment) {
88
+ break;
89
+ }
90
+ node = nextNode;
91
+ }
92
+}
93
+
94
+function removeDelimitedText(parentNode, startNode, closingComment) {
95
+ while (true) {
96
+ var node = startNode.nextSibling;
97
+ if (node === closingComment) {
98
+ // The closing comment is removed by ReactMultiChild.
99
+ break;
100
+ } else {
101
+ parentNode.removeChild(node);
102
+ }
103
+ }
104
+}
105
+
106
+function replaceDelimitedText(openingComment, closingComment, stringText) {
107
+ var parentNode = openingComment.parentNode;
108
+ var nodeAfterComment = openingComment.nextSibling;
109
+ if (nodeAfterComment === closingComment) {
110
+ // There are no text nodes between the opening and closing comments; insert
111
+ // a new one if stringText isn't empty.
112
+ if (stringText) {
113
+ insertChildAt(
114
+ parentNode,
115
+ document.createTextNode(stringText),
116
+ nodeAfterComment
117
+ );
118
+ }
119
+ } else {
120
+ if (stringText) {
121
+ // Set the text content of the first node after the opening comment, and
122
+ // remove all following nodes up until the closing comment.
123
+ setTextContent(nodeAfterComment, stringText);
124
+ removeDelimitedText(parentNode, nodeAfterComment, closingComment);
125
+ } else {
126
+ removeDelimitedText(parentNode, openingComment, closingComment);
127
+ }
128
+ }
129
+}
130
+
131
/**
132
* Operations for updating with DOM children.
133
*/
@@ -60,6 +137,8 @@ var DOMChildrenOperations = {
137
138
updateTextContent: setTextContent,
139
140
+ replaceDelimitedText: replaceDelimitedText,
141
+
142
/**
143
* Updates a component's children by processing a series of updates. The
144
* update configurations are each expected to have a `parentNode` property.
@@ -79,7 +158,7 @@ var DOMChildrenOperations = {
158
);
159
break;
160
case ReactMultiChildUpdateTypes.MOVE_EXISTING:
82
- insertChildAt(
161
+ moveChild(
162
parentNode,
163
update.fromNode,
164
getNodeAfter(parentNode, update.afterNode)
@@ -98,7 +177,7 @@ var DOMChildrenOperations = {
177
);
178
break;
179
case ReactMultiChildUpdateTypes.REMOVE_NODE:
101
- parentNode.removeChild(update.fromNode);
180
+ removeChild(parentNode, update.fromNode);
181
break;
182
}
183
}
@@ -108,6 +187,7 @@ var DOMChildrenOperations = {
187
188
ReactPerf.measureMethods(DOMChildrenOperations, 'DOMChildrenOperations', {
189
updateTextContent: 'updateTextContent',
190
+ replaceDelimitedText: 'replaceDelimitedText',
191
});
192
193
module.exports = DOMChildrenOperations;
src/renderers/dom/client/validateDOMNesting.js
+9
-4
@@ -384,6 +384,11 @@ if (__DEV__) {
384
}
385
didWarn[warnKey] = true;
386
387
+ var tagDisplayName = childTag;
388
+ if (childTag !== '#text') {
389
+ tagDisplayName = '<' + childTag + '>';
390
+ }
391
+
392
if (invalidParent) {
393
var info = '';
394
if (ancestorTag === 'table' && childTag === 'tr') {
@@ -393,9 +398,9 @@ if (__DEV__) {
398
}
399
warning(
400
false,
396
- 'validateDOMNesting(...): <%s> cannot appear as a child of <%s>. ' +
401
+ 'validateDOMNesting(...): %s cannot appear as a child of <%s>. ' +
402
'See %s.%s',
398
- childTag,
403
+ tagDisplayName,
404
ancestorTag,
405
ownerInfo,
406
info
@@ -403,9 +408,9 @@ if (__DEV__) {
408
} else {
409
warning(
410
false,
406
- 'validateDOMNesting(...): <%s> cannot appear as a descendant of ' +
411
+ 'validateDOMNesting(...): %s cannot appear as a descendant of ' +
412
'<%s>. See %s.',
408
- childTag,
413
+ tagDisplayName,
414
ancestorTag,
415
ownerInfo
416
);
src/renderers/dom/server/__tests__/ReactServerRendering-test.js
+4
-4
@@ -103,8 +103,8 @@ describe('ReactServerRendering', function() {
103
ID_ATTRIBUTE_NAME + '="[^"]+" ' +
104
ReactMarkupChecksum.CHECKSUM_ATTR_NAME + '="[^"]+">' +
105
'<span ' + ID_ATTRIBUTE_NAME + '="[^"]+">' +
106
- '<span ' + ID_ATTRIBUTE_NAME + '="[^"]+">My name is </span>' +
107
- '<span ' + ID_ATTRIBUTE_NAME + '="[^"]+">child</span>' +
106
+ '<!-- react-text: [0-9]+ -->My name is <!-- /react-text -->' +
107
+ '<!-- react-text: [0-9]+ -->child<!-- /react-text -->' +
108
'</span>' +
109
'</div>'
110
);
@@ -153,8 +153,8 @@ describe('ReactServerRendering', function() {
153
'<span ' + ROOT_ATTRIBUTE_NAME + '="" ' +
154
ID_ATTRIBUTE_NAME + '="[^"]+" ' +
155
ReactMarkupChecksum.CHECKSUM_ATTR_NAME + '="[^"]+">' +
156
- '<span ' + ID_ATTRIBUTE_NAME + '="[^"]+">Component name: </span>' +
157
- '<span ' + ID_ATTRIBUTE_NAME + '="[^"]+">TestComponent</span>' +
156
+ '<!-- react-text: [0-9]+ -->Component name: <!-- /react-text -->' +
157
+ '<!-- react-text: [0-9]+ -->TestComponent<!-- /react-text -->' +
158
'</span>'
159
);
160
expect(lifecycle).toEqual(
src/renderers/dom/shared/ReactDOMTextComponent.js
+40
-18
@@ -13,7 +13,6 @@
13
14
var DOMChildrenOperations = require('DOMChildrenOperations');
15
var DOMLazyTree = require('DOMLazyTree');
16
-var DOMPropertyOperations = require('DOMPropertyOperations');
16
var ReactDOMComponentTree = require('ReactDOMComponentTree');
17
var ReactPerf = require('ReactPerf');
18
@@ -21,16 +20,14 @@ var assign = require('Object.assign');
20
var escapeTextContentForBrowser = require('escapeTextContentForBrowser');
21
var validateDOMNesting = require('validateDOMNesting');
22
24
-var getNode = ReactDOMComponentTree.getNodeFromInstance;
25
-
23
/**
24
* Text nodes violate a couple assumptions that React makes about components:
25
*
26
* - When mounting text into the DOM, adjacent text nodes are merged.
27
* - Text nodes cannot be assigned a React root ID.
28
*
32
- * This component is used to wrap strings in elements so that they can undergo
33
- * the same reconciliation that is applied to elements.
29
+ * This component is used to wrap strings between comment nodes so that they
30
+ * can undergo the same reconciliation that is applied to elements.
31
*
32
* TODO: Investigate representing React components in the DOM with text nodes.
33
*
@@ -49,6 +46,8 @@ var ReactDOMTextComponent = function(text) {
46
// Properties
47
this._domID = null;
48
this._mountIndex = 0;
49
+ this._openingComment = null;
50
+ this._commentNodes = null;
51
};
52
53
assign(ReactDOMTextComponent.prototype, {
@@ -77,34 +76,44 @@ assign(ReactDOMTextComponent.prototype, {
76
if (parentInfo) {
77
// parentInfo should always be present except for the top-level
78
// component when server rendering
80
- validateDOMNesting('span', this, parentInfo);
79
+ validateDOMNesting('#text', this, parentInfo);
80
}
81
}
82
83
var domID = nativeContainerInfo._idCounter++;
84
+ var openingValue = ' react-text: ' + domID + ' ';
85
+ var closingValue = ' /react-text ';
86
this._domID = domID;
87
this._nativeParent = nativeParent;
88
if (transaction.useCreateElement) {
89
var ownerDocument = nativeContainerInfo._ownerDocument;
89
- var el = ownerDocument.createElement('span');
90
- ReactDOMComponentTree.precacheNode(this, el);
91
- var lazyTree = DOMLazyTree(el);
92
- DOMLazyTree.queueText(lazyTree, this._stringText);
90
+ var openingComment = ownerDocument.createComment(openingValue);
91
+ var closingComment = ownerDocument.createComment(closingValue);
92
+ var lazyTree = DOMLazyTree(ownerDocument.createDocumentFragment());
93
+ DOMLazyTree.queueChild(lazyTree, DOMLazyTree(openingComment));
94
+ if (this._stringText) {
95
+ DOMLazyTree.queueChild(
96
+ lazyTree,
97
+ DOMLazyTree(ownerDocument.createTextNode(this._stringText))
98
+ );
99
+ }
100
+ DOMLazyTree.queueChild(lazyTree, DOMLazyTree(closingComment));
101
+ this._openingComment = openingComment;
102
+ ReactDOMComponentTree.precacheNode(this, closingComment);
103
return lazyTree;
104
} else {
105
var escapedText = escapeTextContentForBrowser(this._stringText);
106
107
if (transaction.renderToStaticMarkup) {
98
- // Normally we'd wrap this in a `span` for the reasons stated above, but
99
- // since this is a situation where React won't take over (static pages),
100
- // we can simply return the text as it is.
108
+ // Normally we'd wrap this between comment nodes for the reasons stated
109
+ // above, but since this is a situation where React won't take over
110
+ // (static pages), we can simply return the text as it is.
111
return escapedText;
112
}
113
114
return (
105
- '<span ' + DOMPropertyOperations.createMarkupForID(domID) + '>' +
106
- escapedText +
107
- '</span>'
115
+ '<!--' + openingValue + '-->' + escapedText +
116
+ '<!--' + closingValue + '-->'
117
);
118
}
119
},
@@ -125,16 +134,29 @@ assign(ReactDOMTextComponent.prototype, {
134
// and/or updateComponent to do the actual update for consistency with
135
// other component types?
136
this._stringText = nextStringText;
128
- DOMChildrenOperations.updateTextContent(getNode(this), nextStringText);
137
+ var commentNodes = this.getNativeNode();
138
+ DOMChildrenOperations.replaceDelimitedText(
139
+ commentNodes[0],
140
+ commentNodes[1],
141
+ nextStringText
142
+ );
143
}
144
}
145
},
146
147
getNativeNode: function() {
134
- return getNode(this);
148
+ var nativeNode = this._commentNodes;
149
+ if (nativeNode) {
150
+ return nativeNode;
151
+ }
152
+ nativeNode = [this._openingComment, this._nativeNode];
153
+ this._commentNodes = nativeNode;
154
+ return nativeNode;
155
},
156
157
unmountComponent: function() {
158
+ this._openingComment = null;
159
+ this._commentNodes = null;
160
ReactDOMComponentTree.uncacheNode(this);
161
},
162
src/renderers/dom/shared/__tests__/ReactDOMComponent-test.js
+2
-2
@@ -1260,8 +1260,8 @@ describe('ReactDOMComponent', function() {
1260
'match the DOM tree generated by the browser.'
1261
);
1262
expect(console.error.argsForCall[1][0]).toBe(
1263
- 'Warning: validateDOMNesting(...): <span> cannot appear as a child ' +
1264
- 'of <table>. See Foo > table > span.'
1263
+ 'Warning: validateDOMNesting(...): #text cannot appear as a child ' +
1264
+ 'of <table>. See Foo > table > #text.'
1265
);
1266
});
1267
src/renderers/dom/shared/__tests__/ReactDOMTextComponent-test.js
+82
-12
@@ -13,26 +13,96 @@
13
14
var React;
15
var ReactDOM;
16
+var ReactDOMServer;
17
18
describe('ReactDOMTextComponent', function() {
19
beforeEach(function() {
20
React = require('React');
21
ReactDOM = require('ReactDOM');
22
+ ReactDOMServer = require('ReactDOMServer');
23
});
24
25
it('updates a mounted text component in place', function() {
26
var el = document.createElement('div');
25
- var inst = ReactDOM.render(<div>{'foo'}{'bar'}</div>, el);
26
-
27
- var foo = ReactDOM.findDOMNode(inst).children[0];
28
- var bar = ReactDOM.findDOMNode(inst).children[1];
29
- expect(foo.tagName).toBe('SPAN');
30
- expect(bar.tagName).toBe('SPAN');
31
-
32
- inst = ReactDOM.render(<div>{'baz'}{'qux'}</div>, el);
33
- // After the update, the spans should have stayed in place (as opposed to
34
- // getting unmounted and remounted)
35
- expect(ReactDOM.findDOMNode(inst).children[0]).toBe(foo);
36
- expect(ReactDOM.findDOMNode(inst).children[1]).toBe(bar);
27
+ var inst = ReactDOM.render(<div><span />{'foo'}{'bar'}</div>, el);
28
+
29
+ var foo = ReactDOM.findDOMNode(inst).childNodes[2];
30
+ var bar = ReactDOM.findDOMNode(inst).childNodes[5];
31
+ expect(foo.data).toBe('foo');
32
+ expect(bar.data).toBe('bar');
33
+
34
+ inst = ReactDOM.render(<div><span />{'baz'}{'qux'}</div>, el);
35
+ // After the update, the text nodes should have stayed in place (as opposed
36
+ // to getting unmounted and remounted)
37
+ expect(ReactDOM.findDOMNode(inst).childNodes[2]).toBe(foo);
38
+ expect(ReactDOM.findDOMNode(inst).childNodes[5]).toBe(bar);
39
+ expect(foo.data).toBe('baz');
40
+ expect(bar.data).toBe('qux');
41
+ });
42
+
43
+ it('can be toggled in and out of the markup', function() {
44
+ var el = document.createElement('div');
45
+ var inst = ReactDOM.render(<div>{'foo'}<div />{'bar'}</div>, el);
46
+
47
+ var container = ReactDOM.findDOMNode(inst);
48
+ var childDiv = container.childNodes[3];
49
+ var childNodes;
50
+
51
+ inst = ReactDOM.render(<div>{null}<div />{null}</div>, el);
52
+ container = ReactDOM.findDOMNode(inst);
53
+ childNodes = container.childNodes;
54
+ expect(childNodes.length).toBe(1);
55
+ expect(childNodes[0]).toBe(childDiv);
56
+
57
+ inst = ReactDOM.render(<div>{'foo'}<div />{'bar'}</div>, el);
58
+ container = ReactDOM.findDOMNode(inst);
59
+ childNodes = container.childNodes;
60
+ expect(childNodes.length).toBe(7);
61
+ expect(childNodes[1].data).toBe('foo');
62
+ expect(childNodes[3]).toBe(childDiv);
63
+ expect(childNodes[5].data).toBe('bar');
64
+ });
65
+
66
+ it('can reconcile text merged by Node.normalize()', function() {
67
+ var el = document.createElement('div');
68
+ var inst = ReactDOM.render(<div>{'foo'}{'bar'}{'baz'}</div>, el);
69
+
70
+ var container = ReactDOM.findDOMNode(inst);
71
+ container.normalize();
72
+
73
+ inst = ReactDOM.render(<div>{'bar'}{'baz'}{'qux'}</div>, el);
74
+ container = ReactDOM.findDOMNode(inst);
75
+ expect(container.textContent).toBe('barbazqux');
76
+ });
77
+
78
+ it('can reconcile text from pre-rendered markup', function() {
79
+ var el = document.createElement('div');
80
+ var reactEl = <div>{'foo'}{'bar'}{'baz'}</div>;
81
+ el.innerHTML = ReactDOMServer.renderToString(reactEl);
82
+
83
+ ReactDOM.render(reactEl, el);
84
+ expect(el.textContent).toBe('foobarbaz');
85
+
86
+ reactEl = <div>{''}{''}{''}</div>;
87
+ el.innerHTML = ReactDOMServer.renderToString(reactEl);
88
+
89
+ ReactDOM.render(reactEl, el);
90
+ expect(el.textContent).toBe('');
91
+ });
92
+
93
+ it('can reconcile text arbitrarily split into multiple nodes', function() {
94
+ var el = document.createElement('div');
95
+ var inst = ReactDOM.render(<div><span />{'foobarbaz'}</div>, el);
96
+
97
+ var container = ReactDOM.findDOMNode(inst);
98
+ var childNodes = container.childNodes;
99
+ var textNode = childNodes[2];
100
+ textNode.textContent = 'foo';
101
+ container.insertBefore(document.createTextNode('bar'), childNodes[3]);
102
+ container.insertBefore(document.createTextNode('baz'), childNodes[3]);
103
+
104
+ inst = ReactDOM.render(<div><span />{'barbazqux'}</div>, el);
105
+ container = ReactDOM.findDOMNode(inst);
106
+ expect(container.textContent).toBe('barbazqux');
107
});
108
});
src/renderers/shared/reconciler/__tests__/ReactMultiChildText-test.js
+55
-5
@@ -49,24 +49,40 @@ var expectChildren = function(d, children) {
49
expect(textNode.data).toBe('' + children);
50
}
51
} else {
52
- expect(outerNode.childNodes.length).toBe(children.length);
52
+ var openingCommentNode;
53
+ var closingCommentNode;
54
+ var mountIndex = 0;
55
56
for (var i = 0; i < children.length; i++) {
57
var child = children[i];
58
59
if (typeof child === 'string') {
58
- textNode = outerNode.childNodes[i].firstChild;
60
+ openingCommentNode = outerNode.childNodes[mountIndex];
61
+
62
+ expect(openingCommentNode.nodeType).toBe(8);
63
+ expect(openingCommentNode.nodeValue).toMatch(' react-text: [0-9]+ ');
64
65
if (child === '') {
61
- expect(textNode).toBe(null);
66
+ textNode = null;
67
+ closingCommentNode = openingCommentNode.nextSibling;
68
+ mountIndex += 2;
69
} else {
63
- expect(textNode).not.toBe(null);
70
+ textNode = openingCommentNode.nextSibling;
71
+ closingCommentNode = textNode.nextSibling;
72
+ mountIndex += 3;
73
+ }
74
+
75
+ if (textNode) {
76
expect(textNode.nodeType).toBe(3);
77
expect(textNode.data).toBe('' + child);
78
}
79
+
80
+ expect(closingCommentNode.nodeType).toBe(8);
81
+ expect(closingCommentNode.nodeValue).toBe(' /react-text ');
82
} else {
68
- var elementDOMNode = outerNode.childNodes[i];
83
+ var elementDOMNode = outerNode.childNodes[mountIndex];
84
expect(elementDOMNode.tagName).toBe('DIV');
85
+ mountIndex++;
86
}
87
}
88
}
@@ -194,4 +210,38 @@ describe('ReactMultiChildText', function() {
210
ReactTestUtils.renderIntoDocument(<div><h1>{['A', 'B']}</h1></div>);
211
}).not.toThrow();
212
});
213
+
214
+ it('should reorder keyed text nodes', function() {
215
+ spyOn(console, 'error');
216
+
217
+ var container = document.createElement('div');
218
+ ReactDOM.render(
219
+ <div>{new Map([['a', 'alpha'], ['b', 'beta']])}</div>,
220
+ container
221
+ );
222
+
223
+ var childNodes = container.firstChild.childNodes;
224
+ var alpha1 = childNodes[0];
225
+ var alpha2 = childNodes[1];
226
+ var alpha3 = childNodes[2];
227
+ var beta1 = childNodes[3];
228
+ var beta2 = childNodes[4];
229
+ var beta3 = childNodes[5];
230
+
231
+ ReactDOM.render(
232
+ <div>{new Map([['b', 'beta'], ['a', 'alpha']])}</div>,
233
+ container
234
+ );
235
+
236
+ childNodes = container.firstChild.childNodes;
237
+ expect(childNodes[0]).toBe(beta1);
238
+ expect(childNodes[1]).toBe(beta2);
239
+ expect(childNodes[2]).toBe(beta3);
240
+ expect(childNodes[3]).toBe(alpha1);
241
+ expect(childNodes[4]).toBe(alpha2);
242
+ expect(childNodes[5]).toBe(alpha3);
243
+
244
+ // Using Maps as children gives a single warning
245
+ expect(console.error.calls.length).toBe(1);
246
+ });
247
});
src/test/ReactDefaultPerf.js
+13
-3
@@ -27,7 +27,7 @@ function addValue(obj, key, val) {
27
obj[key] = (obj[key] || 0) + val;
28
}
29
30
-// Composites don't have any built-in ID: we have to make our own
30
+// Composite/text components don't have any built-in ID: we have to make our own
31
var compositeIDMap;
32
var compositeIDCounter = 17000;
33
function getIDOfComposite(inst) {
@@ -43,6 +43,14 @@ function getIDOfComposite(inst) {
43
}
44
}
45
46
+function getID(inst) {
47
+ if (inst.hasOwnProperty('_rootNodeID')) {
48
+ return inst._rootNodeID;
49
+ } else {
50
+ return getIDOfComposite(inst);
51
+ }
52
+}
53
+
54
var ReactDefaultPerf = {
55
_allMeasurements: [], // last item in the list is the current one
56
_mountStack: [0],
@@ -224,8 +232,10 @@ var ReactDefaultPerf = {
232
} else if (fnName === 'replaceNodeWithMarkup') {
233
// Old node is already unmounted; can't get its instance
234
id = ReactDOMComponentTree.getInstanceFromNode(args[1].node)._rootNodeID;
235
+ } else if (fnName === 'replaceDelimitedText') {
236
+ id = getID(ReactDOMComponentTree.getInstanceFromNode(args[1]));
237
} else if (typeof id === 'object') {
228
- id = ReactDOMComponentTree.getInstanceFromNode(args[0])._rootNodeID;
238
+ id = getID(ReactDOMComponentTree.getInstanceFromNode(args[0]));
239
}
240
ReactDefaultPerf._recordWrite(
241
id,
@@ -291,7 +301,7 @@ var ReactDefaultPerf = {
301
fnName === 'receiveComponent')) {
302
303
rv = func.apply(this, args);
294
- entry.hierarchy[this._rootNodeID] =
304
+ entry.hierarchy[getID(this)] =
305
ReactDefaultPerf._compositeStack.slice();
306
return rv;
307
} else {
src/test/ReactDefaultPerfAnalysis.js
+1
@@ -27,6 +27,7 @@ var DOM_OPERATION_TYPES = {
27
'deleteValueForProperty': 'remove attribute',
28
'setValueForStyles': 'update styles',
29
'replaceNodeWithMarkup': 'replace',
30
+ 'replaceDelimitedText': 'replace',
31
'updateTextContent': 'set textContent',
32
};
33