Extract ReactComponentTreeDevtool
Dan Abramov committed
Apr 21, 2016 at 17:10 UTC
b5997c8e7b7ffe1c49c8a287013ce216b7696fce
2 files changed
+149
-126
src/isomorphic/__tests__/ReactDebugTool-test.js
+45
-126
@@ -17,120 +17,7 @@ describe('ReactDebugTool', () => {
17
var ReactDOM;
18
var ReactDOMServer;
19
var ReactInstanceMap;
20
-
21
- function createDevtool() {
22
- var unmountedContainerIDs = [];
23
- var allChildIDsByContainerID = {};
24
- var tree = {};
25
-
26
- function updateTree(id, update) {
27
- if (!tree[id]) {
28
- tree[id] = {};
29
- }
30
- update(tree[id]);
31
- }
32
-
33
- function getTree(id, includeOwner) {
34
- var item = tree[id];
35
- var result = {
36
- isComposite: item.isComposite,
37
- displayName: item.displayName,
38
- };
39
- if (item.childIDs) {
40
- result.children = item.childIDs.map(childID =>
41
- getTree(childID, includeOwner)
42
- );
43
- }
44
- if (item.text != null) {
45
- result.text = item.text;
46
- }
47
- if (includeOwner && item.ownerDebugID) {
48
- result.ownerDisplayName = tree[item.ownerDebugID].displayName;
49
- }
50
- return result;
51
- }
52
-
53
- function purgeTree(id) {
54
- var item = tree[id];
55
- if (!item) {
56
- return;
57
- }
58
-
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
- }
71
- }
72
-
73
- return {
74
- onSetIsComposite(id, isComposite) {
75
- updateTree(id, item => item.isComposite = isComposite);
76
- },
77
-
78
- onSetDisplayName(id, displayName) {
79
- updateTree(id, item => item.displayName = displayName);
80
- },
81
-
82
- onSetChildren(id, childIDs) {
83
- childIDs.forEach(childID => {
84
- var childItem = tree[childID];
85
- expect(childItem).toBeDefined();
86
- expect(childItem.isComposite).toBeDefined();
87
- expect(childItem.displayName).toBeDefined();
88
- expect(childItem.childIDs || childItem.text).toBeDefined();
89
- });
90
-
91
- updateTree(id, item => item.childIDs = childIDs);
92
- },
93
-
94
- onSetOwner(id, ownerDebugID) {
95
- updateTree(id, item => item.ownerDebugID = ownerDebugID);
96
- },
97
-
98
- onSetText(id, text) {
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
-
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) {
130
- return getTree(rootDebugID, includeOwner);
131
- },
132
- };
133
- }
20
+ var ReactComponentTreeDevtool;
21
22
beforeEach(() => {
23
jest.resetModuleRegistry();
@@ -140,15 +27,48 @@ describe('ReactDebugTool', () => {
27
ReactDOM = require('ReactDOM');
28
ReactDOMServer = require('ReactDOMServer');
29
ReactInstanceMap = require('ReactInstanceMap');
30
+ ReactComponentTreeDevtool = require('ReactComponentTreeDevtool');
31
+
32
+ ReactDebugTool.addDevtool(ReactComponentTreeDevtool);
33
+ });
34
+
35
+ afterEach(() => {
36
+ ReactDebugTool.removeDevtool(ReactComponentTreeDevtool);
37
});
38
39
+ function denormalizeTree(tree, rootID, includeOwner) {
40
+ var item = tree[rootID];
41
+ var result = {
42
+ isComposite: item.isComposite,
43
+ displayName: item.displayName,
44
+ };
45
+ if (item.childIDs) {
46
+ result.children = item.childIDs.map(childID =>
47
+ denormalizeTree(tree, childID, includeOwner)
48
+ );
49
+ }
50
+ if (item.text != null) {
51
+ result.text = item.text;
52
+ }
53
+ if (includeOwner && item.ownerDebugID) {
54
+ result.ownerDisplayName = tree[item.ownerDebugID].displayName;
55
+ }
56
+ return result;
57
+ }
58
+
59
+ function getRegisteredDisplayNames(tree) {
60
+ return Object.keys(tree).map(id => tree[id].displayName);
61
+ }
62
+
63
function assertTreeMatches(pairs, includeOwner) {
64
if (!Array.isArray(pairs[0])) {
65
pairs = [pairs];
66
}
67
68
+ var node = document.createElement('div');
69
var currentElement;
70
var rootInstance;
71
+
72
class Wrapper extends React.Component {
73
render() {
74
rootInstance = ReactInstanceMap.get(this);
@@ -156,33 +76,32 @@ describe('ReactDebugTool', () => {
76
}
77
}
78
159
- var clientDevtool = createDevtool();
160
- ReactDebugTool.addDevtool(clientDevtool);
161
- var node = document.createElement('div');
79
pairs.forEach(([element, expectedTree]) => {
80
currentElement = element;
81
ReactDOM.render(<Wrapper />, node);
165
- expect(clientDevtool.getTree(
82
+ expect(denormalizeTree(
83
+ ReactComponentTreeDevtool.getTree(),
84
rootInstance._renderedComponent._debugID,
85
includeOwner
86
)).toEqual(expectedTree);
87
});
88
ReactDOM.unmountComponentAtNode(node);
171
- ReactDebugTool.removeDevtool(clientDevtool);
172
- expect(clientDevtool.getRegisteredDisplayNames()).toEqual([]);
89
+ expect(
90
+ getRegisteredDisplayNames(ReactComponentTreeDevtool.getTree())
91
+ ).toEqual([]);
92
174
- var serverDevtool = createDevtool();
93
pairs.forEach(([element, expectedTree]) => {
94
currentElement = element;
177
- ReactDebugTool.addDevtool(serverDevtool);
95
ReactDOMServer.renderToString(<Wrapper />);
179
- ReactDebugTool.removeDevtool(serverDevtool);
180
- expect(serverDevtool.getTree(
96
+ expect(denormalizeTree(
97
+ ReactComponentTreeDevtool.getTree(),
98
rootInstance._renderedComponent._debugID,
99
includeOwner
100
)).toEqual(expectedTree);
184
- serverDevtool.purgeUnmountedContainers();
185
- expect(serverDevtool.getRegisteredDisplayNames()).toEqual([]);
101
+ ReactComponentTreeDevtool.purgeUnmountedContainers();
102
+ expect(
103
+ getRegisteredDisplayNames(ReactComponentTreeDevtool.getTree())
104
+ ).toEqual([]);
105
});
106
}
107
src/isomorphic/devtools/ReactComponentTreeDevtool.js
new
+104
@@ -0,0 +1,104 @@
1
+/**
2
+ * Copyright 2016-present, 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 ReactComponentTreeDevtool
10
+ */
11
+
12
+'use strict';
13
+
14
+var unmountedContainerIDs = [];
15
+var allChildIDsByContainerID = {};
16
+var tree = {};
17
+
18
+function updateTree(id, update) {
19
+ if (!tree[id]) {
20
+ tree[id] = {};
21
+ }
22
+ update(tree[id]);
23
+}
24
+
25
+function purgeTree(id) {
26
+ var item = tree[id];
27
+ if (!item) {
28
+ return;
29
+ }
30
+
31
+ var {childIDs, containerID} = item;
32
+ delete tree[id];
33
+
34
+ if (containerID) {
35
+ allChildIDsByContainerID[containerID] = allChildIDsByContainerID[containerID]
36
+ .filter(childID => childID !== id);
37
+ }
38
+
39
+ if (childIDs) {
40
+ childIDs.forEach(purgeTree);
41
+ }
42
+}
43
+
44
+var ReactComponentTreeDevtool = {
45
+ onSetIsComposite(id, isComposite) {
46
+ updateTree(id, item => item.isComposite = isComposite);
47
+ },
48
+
49
+ onSetDisplayName(id, displayName) {
50
+ updateTree(id, item => item.displayName = displayName);
51
+ },
52
+
53
+ onSetChildren(id, childIDs) {
54
+ childIDs.forEach(childID => {
55
+ var childItem = tree[childID];
56
+ expect(childItem).toBeDefined();
57
+ expect(childItem.isComposite).toBeDefined();
58
+ expect(childItem.displayName).toBeDefined();
59
+ expect(childItem.childIDs || childItem.text).toBeDefined();
60
+ });
61
+
62
+ updateTree(id, item => item.childIDs = childIDs);
63
+ },
64
+
65
+ onSetOwner(id, ownerDebugID) {
66
+ updateTree(id, item => item.ownerDebugID = ownerDebugID);
67
+ },
68
+
69
+ onSetText(id, text) {
70
+ updateTree(id, item => item.text = text);
71
+ },
72
+
73
+ onMountComponent(id, containerID) {
74
+ if (!allChildIDsByContainerID[containerID]) {
75
+ allChildIDsByContainerID[containerID] = [];
76
+ }
77
+ allChildIDsByContainerID[containerID].push(id);
78
+ updateTree(id, item => item.containerID = containerID);
79
+ },
80
+
81
+ onUnmountComponent(id) {
82
+ purgeTree(id);
83
+ },
84
+
85
+ onUnmountNativeContainer(containerID) {
86
+ unmountedContainerIDs.push(containerID);
87
+ },
88
+
89
+ purgeUnmountedContainers() {
90
+ unmountedContainerIDs.forEach(containerID => {
91
+ allChildIDsByContainerID[containerID].forEach(purgeTree);
92
+ });
93
+ unmountedContainerIDs = [];
94
+ },
95
+
96
+ getTree() {
97
+ return Object.keys(tree).reduce((result, key) => {
98
+ result[key] = {...tree[key]};
99
+ return result;
100
+ }, {});
101
+ },
102
+};
103
+
104
+module.exports = ReactComponentTreeDevtool;