@samitouri / QOS-React / commits / b5997c8e7b

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;