Add hooks back for devtools
Ben Alpert committed
Nov 3, 2015 at 18:19 UTC
b80f676d275d3a88f4d5524731c5398fa04afab6
5 files changed
+112
-35
src/renderers/dom/ReactDOM.js
+17
@@ -13,6 +13,7 @@
13
14
'use strict';
15
16
+var ReactDOMComponentTree = require('ReactDOMComponentTree');
17
var ReactDefaultInjection = require('ReactDefaultInjection');
18
var ReactMount = require('ReactMount');
19
var ReactPerf = require('ReactPerf');
@@ -21,6 +22,7 @@ var ReactUpdates = require('ReactUpdates');
22
var ReactVersion = require('ReactVersion');
23
24
var findDOMNode = require('findDOMNode');
25
+var getNativeComponentFromComposite = require('getNativeComponentFromComposite');
26
var renderSubtreeIntoContainer = require('renderSubtreeIntoContainer');
27
var warning = require('warning');
28
@@ -46,6 +48,21 @@ if (
48
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ !== 'undefined' &&
49
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.inject === 'function') {
50
__REACT_DEVTOOLS_GLOBAL_HOOK__.inject({
51
+ ComponentTree: {
52
+ getClosestInstanceFromNode:
53
+ ReactDOMComponentTree.getClosestInstanceFromNode,
54
+ getNodeFromInstance: function(inst) {
55
+ // inst is an internal instance (but could be a composite)
56
+ if (inst._renderedComponent) {
57
+ inst = getNativeComponentFromComposite(inst);
58
+ }
59
+ if (inst) {
60
+ return ReactDOMComponentTree.getNodeFromInstance(inst);
61
+ } else {
62
+ return null;
63
+ }
64
+ },
65
+ },
66
Mount: ReactMount,
67
Reconciler: ReactReconciler,
68
});
src/renderers/dom/client/ReactMount.js
+27
-5
@@ -39,6 +39,8 @@ var ELEMENT_NODE_TYPE = 1;
39
var DOC_NODE_TYPE = 9;
40
var DOCUMENT_FRAGMENT_NODE_TYPE = 11;
41
42
+var instancesByReactRootID = {};
43
+
44
/**
45
* Finds the index of the first character
46
* that's not common between the two given strings.
@@ -181,12 +183,19 @@ function hasNonRootReactChild(container) {
183
);
184
}
185
184
-function getTopLevelWrapperInContainer(container) {
186
+function getNativeRootInstanceInContainer(container) {
187
var rootEl = getReactRootElementInContainer(container);
188
var prevNativeInstance =
189
rootEl && ReactDOMComponentTree.getInstanceFromNode(rootEl);
188
- return !prevNativeInstance || prevNativeInstance._nativeParent ? null :
189
- prevNativeInstance._nativeContainerInfo._topLevelWrapper;
190
+ return (
191
+ prevNativeInstance && !prevNativeInstance._nativeParent ?
192
+ prevNativeInstance : null
193
+ );
194
+}
195
+
196
+function getTopLevelWrapperInContainer(container) {
197
+ var root = getNativeRootInstanceInContainer(container);
198
+ return root ? root._nativeContainerInfo._topLevelWrapper : null;
199
}
200
201
/**
@@ -194,7 +203,10 @@ function getTopLevelWrapperInContainer(container) {
203
* composites instead of having to worry about different types of components
204
* here.
205
*/
197
-var TopLevelWrapper = function() {};
206
+var topLevelRootCounter = 1;
207
+var TopLevelWrapper = function() {
208
+ this.rootID = topLevelRootCounter++;
209
+};
210
TopLevelWrapper.prototype.isReactComponent = {};
211
if (__DEV__) {
212
TopLevelWrapper.displayName = 'TopLevelWrapper';
@@ -226,6 +238,11 @@ var ReactMount = {
238
239
TopLevelWrapper: TopLevelWrapper,
240
241
+ /**
242
+ * Used by devtools. The keys are not important.
243
+ */
244
+ _instancesByReactRootID: instancesByReactRootID,
245
+
246
/**
247
* This is a hook provided to support rendering React components while
248
* ensuring that the apparent scroll position of its `container` does not
@@ -261,7 +278,8 @@ var ReactMount = {
278
},
279
280
/**
264
- * Render a new component into the DOM.
281
+ * Render a new component into the DOM. Hooked by devtools!
282
+ *
283
* @param {ReactElement} nextElement element to render
284
* @param {DOMElement} container container to render into
285
* @param {boolean} shouldReuseMarkup if we should skip the markup insertion
@@ -310,6 +328,9 @@ var ReactMount = {
328
context
329
);
330
331
+ var wrapperID = componentInstance._instance.rootID;
332
+ instancesByReactRootID[wrapperID] = componentInstance;
333
+
334
return componentInstance;
335
},
336
@@ -526,6 +547,7 @@ var ReactMount = {
547
548
return false;
549
}
550
+ delete instancesByReactRootID[prevComponent._instance.rootID];
551
ReactUpdates.batchedUpdates(
552
unmountComponentFromNode,
553
prevComponent,
src/renderers/dom/client/__tests__/ReactMount-test.js
+37
-15
@@ -11,23 +11,33 @@
11
12
'use strict';
13
14
+var React;
15
+var ReactDOM;
16
+var ReactDOMServer;
17
+var ReactMount;
18
+var ReactTestUtils;
19
+var WebComponents;
20
+
21
describe('ReactMount', function() {
15
- var React = require('React');
16
- var ReactDOM = require('ReactDOM');
17
- var ReactDOMServer = require('ReactDOMServer');
18
- var ReactMount = require('ReactMount');
19
- var ReactTestUtils = require('ReactTestUtils');
20
- var WebComponents = WebComponents;
21
-
22
- try {
23
- if (WebComponents === undefined && typeof jest !== 'undefined') {
24
- WebComponents = require('WebComponents');
22
+ beforeEach(function() {
23
+ jest.resetModuleRegistry();
24
+
25
+ React = require('React');
26
+ ReactDOM = require('ReactDOM');
27
+ ReactDOMServer = require('ReactDOMServer');
28
+ ReactMount = require('ReactMount');
29
+ ReactTestUtils = require('ReactTestUtils');
30
+
31
+ try {
32
+ if (WebComponents === undefined && typeof jest !== 'undefined') {
33
+ WebComponents = require('WebComponents');
34
+ }
35
+ } catch (e) {
36
+ // Parse error expected on engines that don't support setters
37
+ // or otherwise aren't supportable by the polyfill.
38
+ // Leave WebComponents undefined.
39
}
26
- } catch (e) {
27
- // Parse error expected on engines that don't support setters
28
- // or otherwise aren't supportable by the polyfill.
29
- // Leave WebComponents undefined.
30
- }
40
+ });
41
42
describe('unmountComponentAtNode', function() {
43
it('throws when given a non-node', function() {
@@ -257,4 +267,16 @@ describe('ReactMount', function() {
267
268
expect(calls).toBe(5);
269
});
270
+
271
+ it('tracks root instances', function() {
272
+ // Used by devtools.
273
+ expect(Object.keys(ReactMount._instancesByReactRootID).length).toBe(0);
274
+ ReactTestUtils.renderIntoDocument(<span />);
275
+ expect(Object.keys(ReactMount._instancesByReactRootID).length).toBe(1);
276
+ var container = document.createElement('div');
277
+ ReactDOM.render(<span />, container);
278
+ expect(Object.keys(ReactMount._instancesByReactRootID).length).toBe(2);
279
+ ReactDOM.unmountComponentAtNode(container);
280
+ expect(Object.keys(ReactMount._instancesByReactRootID).length).toBe(1);
281
+ });
282
});
src/renderers/dom/client/findDOMNode.js
+1
-15
@@ -15,25 +15,11 @@
15
var ReactCurrentOwner = require('ReactCurrentOwner');
16
var ReactDOMComponentTree = require('ReactDOMComponentTree');
17
var ReactInstanceMap = require('ReactInstanceMap');
18
-var ReactNodeTypes = require('ReactNodeTypes');
18
19
+var getNativeComponentFromComposite = require('getNativeComponentFromComposite');
20
var invariant = require('invariant');
21
var warning = require('warning');
22
23
-function getNativeComponentFromComposite(inst) {
24
- var type;
25
-
26
- while ((type = inst._renderedNodeType) === ReactNodeTypes.COMPOSITE) {
27
- inst = inst._renderedComponent;
28
- }
29
-
30
- if (type === ReactNodeTypes.NATIVE) {
31
- return inst._renderedComponent;
32
- } else if (type === ReactNodeTypes.EMPTY) {
33
- return null;
34
- }
35
-}
36
-
23
/**
24
* Returns the DOM node rendered by this element.
25
*
src/shared/utils/getNativeComponentFromComposite.js
new
+30
@@ -0,0 +1,30 @@
1
+/**
2
+ * Copyright 2013-2015, Facebook, Inc.
3
+ * All rights reserved.
4
+ *
5
+ * This source code is licensed under the BSD-style license found in the
6
+ * LICENSE file in the root directory of this source tree. An additional grant
7
+ * of patent rights can be found in the PATENTS file in the same directory.
8
+ *
9
+ * @providesModule getNativeComponentFromComposite
10
+ */
11
+
12
+'use strict';
13
+
14
+var ReactNodeTypes = require('ReactNodeTypes');
15
+
16
+function getNativeComponentFromComposite(inst) {
17
+ var type;
18
+
19
+ while ((type = inst._renderedNodeType) === ReactNodeTypes.COMPOSITE) {
20
+ inst = inst._renderedComponent;
21
+ }
22
+
23
+ if (type === ReactNodeTypes.NATIVE) {
24
+ return inst._renderedComponent;
25
+ } else if (type === ReactNodeTypes.EMPTY) {
26
+ return null;
27
+ }
28
+}
29
+
30
+module.exports = getNativeComponentFromComposite;