Files
react/src/isomorphic/devtools/ReactComponentTreeDevtool.js
T
Ben Alpert 3cc733add4 Fix severe perf problems in component tree devtool (#6770)
One of the ReactMultiChildText tests renders 2145 roots (and even more components) and unmounts none of them. Now we don't loop through them all a bunch of times so the test takes 20 seconds instead of 60.

We should clean up instantiateReactComponent somehow so that the onSetDisplayName call isn't produced for the TopLevelWrapper, which should allow us to just store an array of unmountedIDs instead of a hash map so we at least don't have double maps. This change mirrors the old logic though.

Reviewers: @gaearon, @sebmarkbage
2016-05-14 12:13:48 -07:00

188 lines
4.3 KiB
JavaScript

/**
* 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 invariant = require('invariant');
var tree = {};
var unmountedIDs = {};
var rootIDs = {};
function updateTree(id, update) {
if (!tree[id]) {
tree[id] = {
element: null,
parentID: null,
ownerID: null,
text: null,
childIDs: [],
displayName: 'Unknown',
isMounted: false,
updateCount: 0,
};
// TODO: We need to do this awkward dance because TopLevelWrapper "never
// gets mounted" but its display name gets set in instantiateReactComponent
// before its debug ID is set to 0.
unmountedIDs[id] = true;
}
update(tree[id]);
}
function purgeDeep(id) {
var item = tree[id];
if (item) {
var {childIDs} = item;
delete tree[id];
childIDs.forEach(purgeDeep);
}
}
var ReactComponentTreeDevtool = {
onSetDisplayName(id, displayName) {
updateTree(id, item => item.displayName = displayName);
},
onSetChildren(id, nextChildIDs) {
updateTree(id, item => {
var prevChildIDs = item.childIDs;
item.childIDs = nextChildIDs;
nextChildIDs.forEach(nextChildID => {
var nextChild = tree[nextChildID];
invariant(
nextChild,
'Expected devtool events to fire for the child ' +
'before its parent includes it in onSetChildren().'
);
invariant(
nextChild.displayName != null,
'Expected onSetDisplayName() to fire for the child ' +
'before its parent includes it in onSetChildren().'
);
invariant(
nextChild.childIDs != null || nextChild.text != null,
'Expected onSetChildren() or onSetText() to fire for the child ' +
'before its parent includes it in onSetChildren().'
);
invariant(
nextChild.isMounted,
'Expected onMountComponent() to fire for the child ' +
'before its parent includes it in onSetChildren().'
);
if (prevChildIDs.indexOf(nextChildID) === -1) {
nextChild.parentID = id;
}
});
});
},
onSetOwner(id, ownerID) {
updateTree(id, item => item.ownerID = ownerID);
},
onSetText(id, text) {
updateTree(id, item => item.text = text);
},
onBeforeMountComponent(id, element) {
updateTree(id, item => item.element = element);
},
onBeforeUpdateComponent(id, element) {
updateTree(id, item => item.element = element);
},
onMountComponent(id) {
updateTree(id, item => item.isMounted = true);
delete unmountedIDs[id];
},
onMountRootComponent(id) {
rootIDs[id] = true;
},
onUpdateComponent(id) {
updateTree(id, item => item.updateCount++);
},
onUnmountComponent(id) {
updateTree(id, item => item.isMounted = false);
unmountedIDs[id] = true;
delete rootIDs[id];
},
purgeUnmountedComponents() {
if (ReactComponentTreeDevtool._preventPurging) {
// Should only be used for testing.
return;
}
for (var id in unmountedIDs) {
purgeDeep(id);
}
unmountedIDs = {};
},
isMounted(id) {
var item = tree[id];
return item ? item.isMounted : false;
},
getChildIDs(id) {
var item = tree[id];
return item ? item.childIDs : [];
},
getDisplayName(id) {
var item = tree[id];
return item ? item.displayName : 'Unknown';
},
getOwnerID(id) {
var item = tree[id];
return item ? item.ownerID : null;
},
getParentID(id) {
var item = tree[id];
return item ? item.parentID : null;
},
getSource(id) {
var item = tree[id];
var element = item ? item.element : null;
var source = element != null ? element._source : null;
return source;
},
getText(id) {
var item = tree[id];
return item ? item.text : null;
},
getUpdateCount(id) {
var item = tree[id];
return item ? item.updateCount : 0;
},
getRootIDs() {
return Object.keys(rootIDs);
},
getRegisteredIDs() {
return Object.keys(tree);
},
};
module.exports = ReactComponentTreeDevtool;