@samitouri / QOS-React / commits / 835ca9bca3

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 };