@samitouri / QOS-React / commits / 1f31357a2e

Remove unnecessary indirection and events from the hooks (#7464)

* Remove unnecessary indirection from the tree hook * Replace onSetDisplayName, onSetOwner, onSetText with one event Less events is better. onSetDisplayName, onSetOwner, and onSetText only existed because we didn't initially track elements. * Remove unused variables

Dan Abramov committed Aug 11, 2016 at 00:19 UTC 1f31357a2e6643ae3ff8e8a583a2c773d7b2209e
7 files changed +108 -137
src/isomorphic/hooks/ReactComponentTreeHook.js
+101 -77
@@ -39,21 +39,16 @@ function remove(id) {
39 delete itemByKey[key];
40 }
41
42 -function update(id, updater) {
42 +function create(id, element) {
43 var key = getKeyFromID(id);
44 - if (!itemByKey[key]) {
45 - itemByKey[key] = {
46 - element: null,
47 - parentID: null,
48 - ownerID: null,
49 - text: null,
50 - childIDs: [],
51 - displayName: 'Unknown',
52 - isMounted: false,
53 - updateCount: 0,
54 - };
55 - }
56 - updater(itemByKey[key]);
44 + itemByKey[key] = {
45 + element,
46 + parentID: null,
47 + text: null,
48 + childIDs: [],
49 + isMounted: false,
50 + updateCount: 0,
51 + };
52 }
53
54 function purgeDeep(id) {
@@ -76,6 +71,18 @@ function describeComponentFrame(name, source, ownerName) {
71 );
72 }
73
74 +function getDisplayName(element) {
75 + if (element == null) {
76 + return '#empty';
77 + } else if (typeof element === 'string' || typeof element === 'number') {
78 + return '#text';
79 + } else if (typeof element.type === 'string') {
80 + return element.type;
81 + } else {
82 + return element.type.displayName || element.type.name || 'Unknown';
83 + }
84 +}
85 +
86 function describeID(id) {
87 var name = ReactComponentTreeHook.getDisplayName(id);
88 var element = ReactComponentTreeHook.getElement(id);
@@ -94,76 +101,74 @@ function describeID(id) {
101 }
102
103 var ReactComponentTreeHook = {
97 - onSetDisplayName(id, displayName) {
98 - update(id, item => item.displayName = displayName);
99 - },
100 -
104 onSetChildren(id, nextChildIDs) {
102 - update(id, item => {
103 - item.childIDs = nextChildIDs;
104 -
105 - nextChildIDs.forEach(nextChildID => {
106 - var nextChild = get(nextChildID);
107 - invariant(
108 - nextChild,
109 - 'Expected hook events to fire for the child ' +
110 - 'before its parent includes it in onSetChildren().'
111 - );
112 - invariant(
113 - nextChild.displayName != null,
114 - 'Expected onSetDisplayName() to fire for the child ' +
115 - 'before its parent includes it in onSetChildren().'
116 - );
117 - invariant(
118 - nextChild.childIDs != null || nextChild.text != null,
119 - 'Expected onSetChildren() or onSetText() to fire for the child ' +
120 - 'before its parent includes it in onSetChildren().'
121 - );
122 - invariant(
123 - nextChild.isMounted,
124 - 'Expected onMountComponent() to fire for the child ' +
125 - 'before its parent includes it in onSetChildren().'
126 - );
127 - if (nextChild.parentID == null) {
128 - nextChild.parentID = id;
129 - // TODO: This shouldn't be necessary but mounting a new root during in
130 - // componentWillMount currently causes not-yet-mounted components to
131 - // be purged from our tree data so their parent ID is missing.
132 - }
133 - invariant(
134 - nextChild.parentID === id,
135 - 'Expected onSetParent() and onSetChildren() to be consistent (%s ' +
136 - 'has parents %s and %s).',
137 - nextChildID,
138 - nextChild.parentID,
139 - id
140 - );
141 - });
142 - });
143 - },
144 -
145 - onSetOwner(id, ownerID) {
146 - update(id, item => item.ownerID = ownerID);
105 + var item = get(id);
106 + item.childIDs = nextChildIDs;
107 +
108 + for (var i = 0; i < nextChildIDs.length; i++) {
109 + var nextChildID = nextChildIDs[i];
110 + var nextChild = get(nextChildID);
111 + invariant(
112 + nextChild,
113 + 'Expected hook events to fire for the child ' +
114 + 'before its parent includes it in onSetChildren().'
115 + );
116 + invariant(
117 + nextChild.childIDs != null ||
118 + typeof nextChild.element !== 'object' ||
119 + nextChild.element == null,
120 + 'Expected onSetChildren() to fire for a container child ' +
121 + 'before its parent includes it in onSetChildren().'
122 + );
123 + invariant(
124 + nextChild.isMounted,
125 + 'Expected onMountComponent() to fire for the child ' +
126 + 'before its parent includes it in onSetChildren().'
127 + );
128 + if (nextChild.parentID == null) {
129 + nextChild.parentID = id;
130 + // TODO: This shouldn't be necessary but mounting a new root during in
131 + // componentWillMount currently causes not-yet-mounted components to
132 + // be purged from our tree data so their parent ID is missing.
133 + }
134 + invariant(
135 + nextChild.parentID === id,
136 + 'Expected onSetParent() and onSetChildren() to be consistent (%s ' +
137 + 'has parents %s and %s).',
138 + nextChildID,
139 + nextChild.parentID,
140 + id
141 + );
142 + }
143 },
144
145 onSetParent(id, parentID) {
150 - update(id, item => item.parentID = parentID);
146 + var item = get(id);
147 + item.parentID = parentID;
148 },
149
153 - onSetText(id, text) {
154 - update(id, item => item.text = text);
150 + onInstantiateComponent(id, element) {
151 + create(id, element);
152 },
153
154 onBeforeMountComponent(id, element) {
158 - update(id, item => item.element = element);
155 + var item = get(id);
156 + item.element = element;
157 },
158
159 onBeforeUpdateComponent(id, element) {
162 - update(id, item => item.element = element);
160 + var item = get(id);
161 + if (!item || !item.isMounted) {
162 + // We may end up here as a result of setState() in componentWillUnmount().
163 + // In this case, ignore the element.
164 + return;
165 + }
166 + item.element = element;
167 },
168
169 onMountComponent(id) {
166 - update(id, item => item.isMounted = true);
170 + var item = get(id);
171 + item.isMounted = true;
172 },
173
174 onMountRootComponent(id) {
@@ -171,11 +176,18 @@ var ReactComponentTreeHook = {
176 },
177
178 onUpdateComponent(id) {
174 - update(id, item => item.updateCount++);
179 + var item = get(id);
180 + if (!item || !item.isMounted) {
181 + // We may end up here as a result of setState() in componentWillUnmount().
182 + // In this case, ignore the element.
183 + return;
184 + }
185 + item.updateCount++;
186 },
187
188 onUnmountComponent(id) {
178 - update(id, item => item.isMounted = false);
189 + var item = get(id);
190 + item.isMounted = false;
191 unmountedIDs[id] = true;
192 delete rootIDs[id];
193 },
@@ -234,8 +246,11 @@ var ReactComponentTreeHook = {
246 },
247
248 getDisplayName(id) {
237 - var item = get(id);
238 - return item ? item.displayName : 'Unknown';
249 + var element = ReactComponentTreeHook.getElement(id);
250 + if (!element) {
251 + return null;
252 + }
253 + return getDisplayName(element);
254 },
255
256 getElement(id) {
@@ -244,8 +259,11 @@ var ReactComponentTreeHook = {
259 },
260
261 getOwnerID(id) {
247 - var item = get(id);
248 - return item ? item.ownerID : null;
262 + var element = ReactComponentTreeHook.getElement(id);
263 + if (!element || !element._owner) {
264 + return null;
265 + }
266 + return element._owner._debugID;
267 },
268
269 getParentID(id) {
@@ -261,8 +279,14 @@ var ReactComponentTreeHook = {
279 },
280
281 getText(id) {
264 - var item = get(id);
265 - return item ? item.text : null;
282 + var element = ReactComponentTreeHook.getElement(id);
283 + if (typeof element === 'string') {
284 + return element;
285 + } else if (typeof element === 'number') {
286 + return '' + element;
287 + } else {
288 + return null;
289 + }
290 },
291
292 getUpdateCount(id) {
src/renderers/dom/shared/ReactDOMComponent.js
+2 -6
@@ -270,16 +270,12 @@ if (__DEV__) {
270 }
271
272 this._contentDebugID = contentDebugID;
273 - var text = '' + content;
274 -
275 - ReactInstrumentation.debugTool.onSetDisplayName(contentDebugID, '#text');
276 - ReactInstrumentation.debugTool.onSetParent(contentDebugID, debugID);
277 - ReactInstrumentation.debugTool.onSetText(contentDebugID, text);
278 -
273 if (hasExistingContent) {
274 ReactInstrumentation.debugTool.onBeforeUpdateComponent(contentDebugID, content);
275 ReactInstrumentation.debugTool.onUpdateComponent(contentDebugID);
276 } else {
277 + ReactInstrumentation.debugTool.onInstantiateComponent(contentDebugID, content);
278 + ReactInstrumentation.debugTool.onSetParent(contentDebugID, debugID);
279 ReactInstrumentation.debugTool.onBeforeMountComponent(contentDebugID, content);
280 ReactInstrumentation.debugTool.onMountComponent(contentDebugID);
281 ReactInstrumentation.debugTool.onSetChildren(debugID, [contentDebugID]);
src/renderers/dom/shared/ReactDOMTextComponent.js
-10
@@ -14,7 +14,6 @@
14 var DOMChildrenOperations = require('DOMChildrenOperations');
15 var DOMLazyTree = require('DOMLazyTree');
16 var ReactDOMComponentTree = require('ReactDOMComponentTree');
17 -var ReactInstrumentation = require('ReactInstrumentation');
17
18 var escapeTextContentForBrowser = require('escapeTextContentForBrowser');
19 var invariant = require('invariant');
@@ -67,8 +66,6 @@ Object.assign(ReactDOMTextComponent.prototype, {
66 context
67 ) {
68 if (__DEV__) {
70 - ReactInstrumentation.debugTool.onSetText(this._debugID, this._stringText);
71 -
69 var parentInfo;
70 if (hostParent != null) {
71 parentInfo = hostParent._ancestorInfo;
@@ -142,13 +139,6 @@ Object.assign(ReactDOMTextComponent.prototype, {
139 commentNodes[1],
140 nextStringText
141 );
145 -
146 - if (__DEV__) {
147 - ReactInstrumentation.debugTool.onSetText(
148 - this._debugID,
149 - nextStringText
150 - );
151 - }
142 }
143 }
144 },
src/renderers/native/ReactNativeTextComponent.js
-11
@@ -11,7 +11,6 @@
11
12 'use strict';
13
14 -var ReactInstrumentation = require('ReactInstrumentation');
14 var ReactNativeComponentTree = require('ReactNativeComponentTree');
15 var ReactNativeTagHandles = require('ReactNativeTagHandles');
16 var UIManager = require('UIManager');
@@ -29,10 +28,6 @@ var ReactNativeTextComponent = function(text) {
28 Object.assign(ReactNativeTextComponent.prototype, {
29
30 mountComponent: function(transaction, hostParent, hostContainerInfo, context) {
32 - if (__DEV__) {
33 - ReactInstrumentation.debugTool.onSetText(this._debugID, this._stringText);
34 - }
35 -
31 // TODO: hostParent should have this context already. Stop abusing context.
32 invariant(
33 context.isInAParentText,
@@ -70,12 +65,6 @@ Object.assign(ReactNativeTextComponent.prototype, {
65 'RCTRawText',
66 {text: this._stringText}
67 );
73 - if (__DEV__) {
74 - ReactInstrumentation.debugTool.onSetText(
75 - this._debugID,
76 - nextStringText
77 - );
78 - }
68 }
69 }
70 },
src/renderers/shared/ReactDebugTool.js
+2 -10
@@ -283,26 +283,18 @@ var ReactDebugTool = {
283 onSetState() {
284 emitEvent('onSetState');
285 },
286 - onSetDisplayName(debugID, displayName) {
287 - checkDebugID(debugID);
288 - emitEvent('onSetDisplayName', debugID, displayName);
289 - },
286 onSetChildren(debugID, childDebugIDs) {
287 checkDebugID(debugID);
288 childDebugIDs.forEach(checkDebugID);
289 emitEvent('onSetChildren', debugID, childDebugIDs);
290 },
295 - onSetOwner(debugID, ownerDebugID) {
296 - checkDebugID(debugID);
297 - emitEvent('onSetOwner', debugID, ownerDebugID);
298 - },
291 onSetParent(debugID, parentDebugID) {
292 checkDebugID(debugID);
293 emitEvent('onSetParent', debugID, parentDebugID);
294 },
303 - onSetText(debugID, text) {
295 + onInstantiateComponent(debugID, element) {
296 checkDebugID(debugID);
305 - emitEvent('onSetText', debugID, text);
297 + emitEvent('onInstantiateComponent', debugID, element);
298 },
299 onMountRootComponent(debugID) {
300 checkDebugID(debugID);
src/renderers/shared/stack/reconciler/instantiateReactComponent.js
+1 -21
@@ -41,21 +41,6 @@ function getDeclarationErrorAddendum(owner) {
41 return '';
42 }
43
44 -function getDisplayName(instance) {
45 - var element = instance._currentElement;
46 - if (element == null) {
47 - return '#empty';
48 - } else if (typeof element === 'string' || typeof element === 'number') {
49 - return '#text';
50 - } else if (typeof element.type === 'string') {
51 - return element.type;
52 - } else if (instance.getName) {
53 - return instance.getName() || 'Unknown';
54 - } else {
55 - return element.type.displayName || element.type.name || 'Unknown';
56 - }
57 -}
58 -
44 /**
45 * Check if the type reference is a known internal type. I.e. not a user
46 * provided composite type.
@@ -144,12 +129,7 @@ function instantiateReactComponent(node, shouldHaveDebugID) {
129 if (shouldHaveDebugID) {
130 var debugID = nextDebugID++;
131 instance._debugID = debugID;
147 - var displayName = getDisplayName(instance);
148 - ReactInstrumentation.debugTool.onSetDisplayName(debugID, displayName);
149 - var owner = node && node._owner;
150 - if (owner) {
151 - ReactInstrumentation.debugTool.onSetOwner(debugID, owner._debugID);
152 - }
132 + ReactInstrumentation.debugTool.onInstantiateComponent(debugID, node);
133 } else {
134 instance._debugID = 0;
135 }
src/test/ReactTestUtils.js
+2 -2
@@ -384,6 +384,7 @@ var NoopInternalComponent = function(element) {
384 this._renderedOutput = element;
385 this._currentElement = element;
386 this._debugID = nextDebugID++;
387 + ReactInstrumentation.debugTool.onInstantiateComponent(this._debugID, element);
388 };
389
390 NoopInternalComponent.prototype = {
@@ -412,8 +413,7 @@ var ShallowComponentWrapper = function(element) {
413 // TODO: Consolidate with instantiateReactComponent
414 if (__DEV__) {
415 this._debugID = nextDebugID++;
415 - var displayName = element.type.displayName || element.type.name || 'Unknown';
416 - ReactInstrumentation.debugTool.onSetDisplayName(this._debugID, displayName);
416 + ReactInstrumentation.debugTool.onInstantiateComponent(this._debugID, element);
417 }
418
419 this.construct(element);