mirror of
https://github.com/zebrajr/react.git
synced 2026-01-15 12:15:22 +00:00
* Add component stack info to key validation warnings * Add `ReactComponentTreeDevtool.getStackAddendumByID`
261 lines
6.3 KiB
JavaScript
261 lines
6.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 ReactCurrentOwner = require('ReactCurrentOwner');
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
function describeComponentFrame(name, source, ownerName) {
|
|
return '\n in ' + name + (
|
|
source ?
|
|
' (at ' + source.fileName.replace(/^.*[\\\/]/, '') + ':' +
|
|
source.lineNumber + ')' :
|
|
ownerName ?
|
|
' (created by ' + ownerName + ')' :
|
|
''
|
|
);
|
|
}
|
|
|
|
function describeID(id) {
|
|
var name = ReactComponentTreeDevtool.getDisplayName(id);
|
|
var element = ReactComponentTreeDevtool.getElement(id);
|
|
var ownerID = ReactComponentTreeDevtool.getOwnerID(id);
|
|
var ownerName;
|
|
if (ownerID) {
|
|
ownerName = ReactComponentTreeDevtool.getDisplayName(ownerID);
|
|
}
|
|
return describeComponentFrame(name, element._source, ownerName);
|
|
}
|
|
|
|
var ReactComponentTreeDevtool = {
|
|
onSetDisplayName(id, displayName) {
|
|
updateTree(id, item => item.displayName = displayName);
|
|
},
|
|
|
|
onSetChildren(id, nextChildIDs) {
|
|
updateTree(id, item => {
|
|
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 (nextChild.parentID == null) {
|
|
nextChild.parentID = id;
|
|
// TODO: This shouldn't be necessary but mounting a new root during in
|
|
// componentWillMount currently causes not-yet-mounted components to
|
|
// be purged from our tree data so their parent ID is missing.
|
|
}
|
|
invariant(
|
|
nextChild.parentID === id,
|
|
'Expected onSetParent() and onSetChildren() to be consistent (%s ' +
|
|
'has parents %s and %s).',
|
|
nextChildID,
|
|
nextChild.parentID,
|
|
id
|
|
);
|
|
});
|
|
});
|
|
},
|
|
|
|
onSetOwner(id, ownerID) {
|
|
updateTree(id, item => item.ownerID = ownerID);
|
|
},
|
|
|
|
onSetParent(id, parentID) {
|
|
updateTree(id, item => item.parentID = parentID);
|
|
},
|
|
|
|
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;
|
|
},
|
|
|
|
getCurrentStackAddendum(topElement) {
|
|
var info = '';
|
|
if (topElement) {
|
|
var type = topElement.type;
|
|
var name = typeof type === 'function' ?
|
|
type.displayName || type.name :
|
|
type;
|
|
var owner = topElement._owner;
|
|
info += describeComponentFrame(
|
|
name || 'Unknown',
|
|
topElement._source,
|
|
owner && owner.getName()
|
|
);
|
|
}
|
|
|
|
var currentOwner = ReactCurrentOwner.current;
|
|
var id = currentOwner && currentOwner._debugID;
|
|
|
|
info += ReactComponentTreeDevtool.getStackAddendumByID(id);
|
|
return info;
|
|
},
|
|
|
|
getStackAddendumByID(id) {
|
|
var info = '';
|
|
while (id) {
|
|
info += describeID(id);
|
|
id = ReactComponentTreeDevtool.getParentID(id);
|
|
}
|
|
return info;
|
|
},
|
|
|
|
getChildIDs(id) {
|
|
var item = tree[id];
|
|
return item ? item.childIDs : [];
|
|
},
|
|
|
|
getDisplayName(id) {
|
|
var item = tree[id];
|
|
return item ? item.displayName : 'Unknown';
|
|
},
|
|
|
|
getElement(id) {
|
|
var item = tree[id];
|
|
return item ? item.element : null;
|
|
},
|
|
|
|
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;
|