Clean up the devtool tree on server rendering
Dan Abramov committed
Apr 20, 2016 at 20:09 UTC
835ca9bca3f3657a7fa0d9e7a019f7e14438a6a6
8 files changed
+90
-27
src/isomorphic/ReactDebugTool.js
+5
-2
@@ -76,8 +76,8 @@ var ReactDebugTool = {
76
onMountRootComponent(debugID) {
77
emitEvent('onMountRootComponent', debugID);
78
},
79
- onMountComponent(debugID) {
80
- emitEvent('onMountComponent', debugID);
79
+ onMountComponent(debugID, nativeContainerDebugID) {
80
+ emitEvent('onMountComponent', debugID, nativeContainerDebugID);
81
},
82
onUpdateComponent(debugID) {
83
emitEvent('onUpdateComponent', debugID);
@@ -85,6 +85,9 @@ var ReactDebugTool = {
85
onUnmountComponent(debugID) {
86
emitEvent('onUnmountComponent', debugID);
87
},
88
+ onUnmountNativeContainer(nativeContainerDebugID) {
89
+ emitEvent('onUnmountNativeContainer', nativeContainerDebugID);
90
+ },
91
};
92
93
ReactDebugTool.addDevtool(ReactInvalidSetStateWarningDevTool);
src/isomorphic/__tests__/ReactDebugTool-test.js
+55
-21
@@ -15,11 +15,12 @@ describe('ReactDebugTool', () => {
15
var React;
16
var ReactDebugTool;
17
var ReactDOM;
18
+ var ReactDOMServer;
19
var ReactInstanceMap;
20
20
- var devtool;
21
-
21
function createDevtool() {
22
+ var unmountedContainerIDs = [];
23
+ var allChildIDsByContainerID = {};
24
var tree = {};
25
26
function updateTree(id, update) {
@@ -55,9 +56,15 @@ describe('ReactDebugTool', () => {
56
return;
57
}
58
58
- var {childIDs} = item;
59
+ var {childIDs, containerID} = item;
60
delete tree[id];
61
62
+ if (containerID) {
63
+ allChildIDsByContainerID[containerID] = allChildIDsByContainerID[containerID].filter(
64
+ childID => childID !== id
65
+ );
66
+ }
67
+
68
if (childIDs) {
69
childIDs.forEach(purgeTree);
70
}
@@ -92,12 +99,31 @@ describe('ReactDebugTool', () => {
99
updateTree(id, item => item.text = text);
100
},
101
102
+ onMountComponent(id, containerID) {
103
+ if (!allChildIDsByContainerID[containerID]) {
104
+ allChildIDsByContainerID[containerID] = [];
105
+ }
106
+ allChildIDsByContainerID[containerID].push(id);
107
+ updateTree(id, item => item.containerID = containerID);
108
+ },
109
+
110
onUnmountComponent(id) {
111
purgeTree(id);
112
},
113
99
- getRegisteredDebugIDs() {
100
- return Object.keys(tree);
114
+ onUnmountNativeContainer(containerID) {
115
+ unmountedContainerIDs.push(containerID);
116
+ },
117
+
118
+ purgeUnmountedContainers() {
119
+ unmountedContainerIDs.forEach(containerID => {
120
+ allChildIDsByContainerID[containerID].forEach(purgeTree);
121
+ });
122
+ unmountedContainerIDs = [];
123
+ },
124
+
125
+ getRegisteredDisplayNames() {
126
+ return Object.keys(tree).map(id => tree[id].displayName);
127
},
128
129
getTree(rootDebugID, includeOwner) {
@@ -112,14 +138,8 @@ describe('ReactDebugTool', () => {
138
React = require('React');
139
ReactDebugTool = require('ReactDebugTool');
140
ReactDOM = require('ReactDOM');
141
+ ReactDOMServer = require('ReactDOMServer');
142
ReactInstanceMap = require('ReactInstanceMap');
116
-
117
- devtool = createDevtool();
118
- ReactDebugTool.addDevtool(devtool);
119
- });
120
-
121
- afterEach(() => {
122
- ReactDebugTool.removeDevtool(devtool);
143
});
144
145
function assertTreeMatches(pairs, includeOwner) {
@@ -128,28 +148,42 @@ describe('ReactDebugTool', () => {
148
}
149
150
var currentElement;
151
+ var rootInstance;
152
class Wrapper extends React.Component {
153
render() {
154
+ rootInstance = ReactInstanceMap.get(this);
155
return currentElement;
156
}
157
}
158
159
+ var clientDevtool = createDevtool();
160
+ ReactDebugTool.addDevtool(clientDevtool);
161
var node = document.createElement('div');
138
-
162
pairs.forEach(([element, expectedTree]) => {
163
currentElement = element;
141
-
142
- var rootPublicInstance = ReactDOM.render(<Wrapper />, node);
143
- var rootInstance = ReactInstanceMap.get(rootPublicInstance);
144
- var actualTree = devtool.getTree(
164
+ ReactDOM.render(<Wrapper />, node);
165
+ expect(clientDevtool.getTree(
166
rootInstance._renderedComponent._debugID,
167
includeOwner
147
- );
148
- expect(actualTree).toEqual(expectedTree);
168
+ )).toEqual(expectedTree);
169
});
150
-
170
ReactDOM.unmountComponentAtNode(node);
152
- expect(devtool.getRegisteredDebugIDs()).toEqual([]);
171
+ ReactDebugTool.removeDevtool(clientDevtool);
172
+ expect(clientDevtool.getRegisteredDisplayNames()).toEqual([]);
173
+
174
+ var serverDevtool = createDevtool();
175
+ pairs.forEach(([element, expectedTree]) => {
176
+ currentElement = element;
177
+ ReactDebugTool.addDevtool(serverDevtool);
178
+ ReactDOMServer.renderToString(<Wrapper />);
179
+ ReactDebugTool.removeDevtool(serverDevtool);
180
+ expect(serverDevtool.getTree(
181
+ rootInstance._renderedComponent._debugID,
182
+ includeOwner
183
+ )).toEqual(expectedTree);
184
+ serverDevtool.purgeUnmountedContainers();
185
+ expect(serverDevtool.getRegisteredDisplayNames()).toEqual([]);
186
+ });
187
}
188
189
describe('mount', () => {
src/renderers/dom/client/ReactMount.js
+5
@@ -576,6 +576,11 @@ var ReactMount = {
576
container,
577
false
578
);
579
+ if (__DEV__) {
580
+ ReactInstrumentation.debugTool.onUnmountNativeContainer(
581
+ prevComponent._nativeContainerInfo._debugID
582
+ );
583
+ }
584
return true;
585
},
586
src/renderers/dom/server/ReactServerRendering.js
+11
-2
@@ -13,7 +13,9 @@
13
var ReactDOMContainerInfo = require('ReactDOMContainerInfo');
14
var ReactDefaultBatchingStrategy = require('ReactDefaultBatchingStrategy');
15
var ReactElement = require('ReactElement');
16
+var ReactInstrumentation = require('ReactInstrumentation');
17
var ReactMarkupChecksum = require('ReactMarkupChecksum');
18
+var ReactReconciler = require('ReactReconciler');
19
var ReactServerBatchingStrategy = require('ReactServerBatchingStrategy');
20
var ReactServerRenderingTransaction =
21
require('ReactServerRenderingTransaction');
@@ -36,12 +38,19 @@ function renderToStringImpl(element, makeStaticMarkup) {
38
39
return transaction.perform(function() {
40
var componentInstance = instantiateReactComponent(element);
39
- var markup = componentInstance.mountComponent(
41
+ var nativeContainerInfo = ReactDOMContainerInfo();
42
+ var markup = ReactReconciler.mountComponent(
43
+ componentInstance,
44
transaction,
45
null,
42
- ReactDOMContainerInfo(),
46
+ nativeContainerInfo,
47
emptyObject
48
);
49
+ if (__DEV__) {
50
+ ReactInstrumentation.debugTool.onUnmountNativeContainer(
51
+ nativeContainerInfo._debugID
52
+ );
53
+ }
54
if (!makeStaticMarkup) {
55
markup = ReactMarkupChecksum.addChecksumToMarkup(markup);
56
}
src/renderers/dom/shared/ReactDOMComponent.js
+7
@@ -719,6 +719,13 @@ ReactDOMComponent.Mixin = {
719
if (contentToUse != null) {
720
// TODO: Validate that text is allowed as a child of this node
721
ret = escapeTextContentForBrowser(contentToUse);
722
+ if (__DEV__) {
723
+ this._contentDebugID = this._debugID + '#text';
724
+ ReactInstrumentation.debugTool.onSetIsComposite(this._contentDebugID, false);
725
+ ReactInstrumentation.debugTool.onSetDisplayName(this._contentDebugID, '#text');
726
+ ReactInstrumentation.debugTool.onSetText(this._contentDebugID, '' + contentToUse);
727
+ ReactInstrumentation.debugTool.onSetChildren(this._debugID, [this._contentDebugID]);
728
+ }
729
} else if (childrenToUse != null) {
730
var mountImages = this.mountChildren(
731
childrenToUse,
src/renderers/dom/shared/ReactDOMContainerInfo.js
+2
@@ -14,6 +14,7 @@
14
var validateDOMNesting = require('validateDOMNesting');
15
16
var DOC_NODE_TYPE = 9;
17
+var nextContainerDebugID = 1;
18
19
function ReactDOMContainerInfo(topLevelWrapper, node) {
20
var info = {
@@ -27,6 +28,7 @@ function ReactDOMContainerInfo(topLevelWrapper, node) {
28
_namespaceURI: node ? node.namespaceURI : null,
29
};
30
if (__DEV__) {
31
+ info._debugID = nextContainerDebugID++;
32
info._ancestorInfo = node ?
33
validateDOMNesting.updatedAncestorInfo(null, info._tag, null) : null;
34
}
src/renderers/shared/reconciler/ReactReconciler.js
+4
-1
@@ -53,7 +53,10 @@ var ReactReconciler = {
53
transaction.getReactMountReady().enqueue(attachRefs, internalInstance);
54
}
55
if (__DEV__) {
56
- ReactInstrumentation.debugTool.onMountComponent(internalInstance._debugID);
56
+ ReactInstrumentation.debugTool.onMountComponent(
57
+ internalInstance._debugID,
58
+ nativeContainerInfo._debugID
59
+ );
60
}
61
return markup;
62
},
src/test/ReactTestUtils.js
+1
-1
@@ -466,7 +466,7 @@ ReactShallowRenderer.prototype._render = function(element, transaction, context)
466
this._instance.receiveComponent(element, transaction, context);
467
} else {
468
var instance = new ShallowComponentWrapper(element);
469
- instance.mountComponent(transaction, null, null, context);
469
+ instance.mountComponent(transaction, null, {}, context);
470
this._instance = instance;
471
}
472
};