From b5997c8e7b7ffe1c49c8a287013ce216b7696fce Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 21 Apr 2016 17:10:22 +0100 Subject: [PATCH] Extract ReactComponentTreeDevtool --- .../__tests__/ReactDebugTool-test.js | 171 +++++------------- .../devtools/ReactComponentTreeDevtool.js | 104 +++++++++++ 2 files changed, 149 insertions(+), 126 deletions(-) create mode 100644 src/isomorphic/devtools/ReactComponentTreeDevtool.js diff --git a/src/isomorphic/__tests__/ReactDebugTool-test.js b/src/isomorphic/__tests__/ReactDebugTool-test.js index 93b0d8ab51..9051583b2e 100644 --- a/src/isomorphic/__tests__/ReactDebugTool-test.js +++ b/src/isomorphic/__tests__/ReactDebugTool-test.js @@ -17,120 +17,7 @@ describe('ReactDebugTool', () => { var ReactDOM; var ReactDOMServer; var ReactInstanceMap; - - function createDevtool() { - var unmountedContainerIDs = []; - var allChildIDsByContainerID = {}; - var tree = {}; - - function updateTree(id, update) { - if (!tree[id]) { - tree[id] = {}; - } - update(tree[id]); - } - - function getTree(id, includeOwner) { - var item = tree[id]; - var result = { - isComposite: item.isComposite, - displayName: item.displayName, - }; - if (item.childIDs) { - result.children = item.childIDs.map(childID => - getTree(childID, includeOwner) - ); - } - if (item.text != null) { - result.text = item.text; - } - if (includeOwner && item.ownerDebugID) { - result.ownerDisplayName = tree[item.ownerDebugID].displayName; - } - return result; - } - - function purgeTree(id) { - var item = tree[id]; - if (!item) { - return; - } - - var {childIDs, containerID} = item; - delete tree[id]; - - if (containerID) { - allChildIDsByContainerID[containerID] = allChildIDsByContainerID[containerID].filter( - childID => childID !== id - ); - } - - if (childIDs) { - childIDs.forEach(purgeTree); - } - } - - return { - onSetIsComposite(id, isComposite) { - updateTree(id, item => item.isComposite = isComposite); - }, - - onSetDisplayName(id, displayName) { - updateTree(id, item => item.displayName = displayName); - }, - - onSetChildren(id, childIDs) { - childIDs.forEach(childID => { - var childItem = tree[childID]; - expect(childItem).toBeDefined(); - expect(childItem.isComposite).toBeDefined(); - expect(childItem.displayName).toBeDefined(); - expect(childItem.childIDs || childItem.text).toBeDefined(); - }); - - updateTree(id, item => item.childIDs = childIDs); - }, - - onSetOwner(id, ownerDebugID) { - updateTree(id, item => item.ownerDebugID = ownerDebugID); - }, - - onSetText(id, text) { - updateTree(id, item => item.text = text); - }, - - onMountComponent(id, containerID) { - if (!allChildIDsByContainerID[containerID]) { - allChildIDsByContainerID[containerID] = []; - } - allChildIDsByContainerID[containerID].push(id); - updateTree(id, item => item.containerID = containerID); - }, - - onUnmountComponent(id) { - purgeTree(id); - }, - - onUnmountNativeContainer(containerID) { - unmountedContainerIDs.push(containerID); - }, - - purgeUnmountedContainers() { - unmountedContainerIDs.forEach(containerID => { - allChildIDsByContainerID[containerID].forEach(purgeTree); - }); - unmountedContainerIDs = []; - }, - - getRegisteredDisplayNames() { - return Object.keys(tree).map(id => tree[id].displayName); - }, - - getTree(rootDebugID, includeOwner) { - return getTree(rootDebugID, includeOwner); - }, - }; - } + var ReactComponentTreeDevtool; beforeEach(() => { jest.resetModuleRegistry(); @@ -140,15 +27,48 @@ describe('ReactDebugTool', () => { ReactDOM = require('ReactDOM'); ReactDOMServer = require('ReactDOMServer'); ReactInstanceMap = require('ReactInstanceMap'); + ReactComponentTreeDevtool = require('ReactComponentTreeDevtool'); + + ReactDebugTool.addDevtool(ReactComponentTreeDevtool); }); + afterEach(() => { + ReactDebugTool.removeDevtool(ReactComponentTreeDevtool); + }); + + function denormalizeTree(tree, rootID, includeOwner) { + var item = tree[rootID]; + var result = { + isComposite: item.isComposite, + displayName: item.displayName, + }; + if (item.childIDs) { + result.children = item.childIDs.map(childID => + denormalizeTree(tree, childID, includeOwner) + ); + } + if (item.text != null) { + result.text = item.text; + } + if (includeOwner && item.ownerDebugID) { + result.ownerDisplayName = tree[item.ownerDebugID].displayName; + } + return result; + } + + function getRegisteredDisplayNames(tree) { + return Object.keys(tree).map(id => tree[id].displayName); + } + function assertTreeMatches(pairs, includeOwner) { if (!Array.isArray(pairs[0])) { pairs = [pairs]; } + var node = document.createElement('div'); var currentElement; var rootInstance; + class Wrapper extends React.Component { render() { rootInstance = ReactInstanceMap.get(this); @@ -156,33 +76,32 @@ describe('ReactDebugTool', () => { } } - var clientDevtool = createDevtool(); - ReactDebugTool.addDevtool(clientDevtool); - var node = document.createElement('div'); pairs.forEach(([element, expectedTree]) => { currentElement = element; ReactDOM.render(, node); - expect(clientDevtool.getTree( + expect(denormalizeTree( + ReactComponentTreeDevtool.getTree(), rootInstance._renderedComponent._debugID, includeOwner )).toEqual(expectedTree); }); ReactDOM.unmountComponentAtNode(node); - ReactDebugTool.removeDevtool(clientDevtool); - expect(clientDevtool.getRegisteredDisplayNames()).toEqual([]); + expect( + getRegisteredDisplayNames(ReactComponentTreeDevtool.getTree()) + ).toEqual([]); - var serverDevtool = createDevtool(); pairs.forEach(([element, expectedTree]) => { currentElement = element; - ReactDebugTool.addDevtool(serverDevtool); ReactDOMServer.renderToString(); - ReactDebugTool.removeDevtool(serverDevtool); - expect(serverDevtool.getTree( + expect(denormalizeTree( + ReactComponentTreeDevtool.getTree(), rootInstance._renderedComponent._debugID, includeOwner )).toEqual(expectedTree); - serverDevtool.purgeUnmountedContainers(); - expect(serverDevtool.getRegisteredDisplayNames()).toEqual([]); + ReactComponentTreeDevtool.purgeUnmountedContainers(); + expect( + getRegisteredDisplayNames(ReactComponentTreeDevtool.getTree()) + ).toEqual([]); }); } diff --git a/src/isomorphic/devtools/ReactComponentTreeDevtool.js b/src/isomorphic/devtools/ReactComponentTreeDevtool.js new file mode 100644 index 0000000000..f5d2b784a5 --- /dev/null +++ b/src/isomorphic/devtools/ReactComponentTreeDevtool.js @@ -0,0 +1,104 @@ +/** + * Copyright 2016-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule ReactComponentTreeDevtool + */ + +'use strict'; + +var unmountedContainerIDs = []; +var allChildIDsByContainerID = {}; +var tree = {}; + +function updateTree(id, update) { + if (!tree[id]) { + tree[id] = {}; + } + update(tree[id]); +} + +function purgeTree(id) { + var item = tree[id]; + if (!item) { + return; + } + + var {childIDs, containerID} = item; + delete tree[id]; + + if (containerID) { + allChildIDsByContainerID[containerID] = allChildIDsByContainerID[containerID] + .filter(childID => childID !== id); + } + + if (childIDs) { + childIDs.forEach(purgeTree); + } +} + +var ReactComponentTreeDevtool = { + onSetIsComposite(id, isComposite) { + updateTree(id, item => item.isComposite = isComposite); + }, + + onSetDisplayName(id, displayName) { + updateTree(id, item => item.displayName = displayName); + }, + + onSetChildren(id, childIDs) { + childIDs.forEach(childID => { + var childItem = tree[childID]; + expect(childItem).toBeDefined(); + expect(childItem.isComposite).toBeDefined(); + expect(childItem.displayName).toBeDefined(); + expect(childItem.childIDs || childItem.text).toBeDefined(); + }); + + updateTree(id, item => item.childIDs = childIDs); + }, + + onSetOwner(id, ownerDebugID) { + updateTree(id, item => item.ownerDebugID = ownerDebugID); + }, + + onSetText(id, text) { + updateTree(id, item => item.text = text); + }, + + onMountComponent(id, containerID) { + if (!allChildIDsByContainerID[containerID]) { + allChildIDsByContainerID[containerID] = []; + } + allChildIDsByContainerID[containerID].push(id); + updateTree(id, item => item.containerID = containerID); + }, + + onUnmountComponent(id) { + purgeTree(id); + }, + + onUnmountNativeContainer(containerID) { + unmountedContainerIDs.push(containerID); + }, + + purgeUnmountedContainers() { + unmountedContainerIDs.forEach(containerID => { + allChildIDsByContainerID[containerID].forEach(purgeTree); + }); + unmountedContainerIDs = []; + }, + + getTree() { + return Object.keys(tree).reduce((result, key) => { + result[key] = {...tree[key]}; + return result; + }, {}); + }, +}; + +module.exports = ReactComponentTreeDevtool;