Merge pull request #6549 from gaearon/instrumentation-new

Provide info about component tree to devtools
This commit is contained in:
Dan Abramov
2016-04-26 01:12:23 +01:00
13 changed files with 2053 additions and 312 deletions
-124
View File
@@ -1,124 +0,0 @@
/**
* 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 ReactDebugInstanceMap
*/
'use strict';
var warning = require('warning');
function checkValidInstance(internalInstance) {
if (!internalInstance) {
warning(
false,
'There is an internal error in the React developer tools integration. ' +
'Instead of an internal instance, received %s. ' +
'Please report this as a bug in React.',
internalInstance
);
return false;
}
var isValid = typeof internalInstance.mountComponent === 'function';
warning(
isValid,
'There is an internal error in the React developer tools integration. ' +
'Instead of an internal instance, received an object with the following ' +
'keys: %s. Please report this as a bug in React.',
Object.keys(internalInstance).join(', ')
);
return isValid;
}
var idCounter = 1;
var instancesByIDs = {};
var instancesToIDs;
function getIDForInstance(internalInstance) {
if (!instancesToIDs) {
instancesToIDs = new WeakMap();
}
if (instancesToIDs.has(internalInstance)) {
return instancesToIDs.get(internalInstance);
} else {
var instanceID = (idCounter++).toString();
instancesToIDs.set(internalInstance, instanceID);
return instanceID;
}
}
function getInstanceByID(instanceID) {
return instancesByIDs[instanceID] || null;
}
function isRegisteredInstance(internalInstance) {
var instanceID = getIDForInstance(internalInstance);
if (instanceID) {
return instancesByIDs.hasOwnProperty(instanceID);
} else {
return false;
}
}
function registerInstance(internalInstance) {
var instanceID = getIDForInstance(internalInstance);
if (instanceID) {
instancesByIDs[instanceID] = internalInstance;
}
}
function unregisterInstance(internalInstance) {
var instanceID = getIDForInstance(internalInstance);
if (instanceID) {
delete instancesByIDs[instanceID];
}
}
var ReactDebugInstanceMap = {
getIDForInstance(internalInstance) {
if (!checkValidInstance(internalInstance)) {
return null;
}
return getIDForInstance(internalInstance);
},
getInstanceByID(instanceID) {
return getInstanceByID(instanceID);
},
isRegisteredInstance(internalInstance) {
if (!checkValidInstance(internalInstance)) {
return false;
}
return isRegisteredInstance(internalInstance);
},
registerInstance(internalInstance) {
if (!checkValidInstance(internalInstance)) {
return;
}
warning(
!isRegisteredInstance(internalInstance),
'There is an internal error in the React developer tools integration. ' +
'A registered instance should not be registered again. ' +
'Please report this as a bug in React.'
);
registerInstance(internalInstance);
},
unregisterInstance(internalInstance) {
if (!checkValidInstance(internalInstance)) {
return;
}
warning(
isRegisteredInstance(internalInstance),
'There is an internal error in the React developer tools integration. ' +
'An unregistered instance should not be unregistered again. ' +
'Please report this as a bug in React.'
);
unregisterInstance(internalInstance);
},
};
module.exports = ReactDebugInstanceMap;
+20 -8
View File
@@ -58,17 +58,29 @@ var ReactDebugTool = {
onSetState() {
emitEvent('onSetState');
},
onMountRootComponent(internalInstance) {
emitEvent('onMountRootComponent', internalInstance);
onSetDisplayName(debugID, displayName) {
emitEvent('onSetDisplayName', debugID, displayName);
},
onMountComponent(internalInstance) {
emitEvent('onMountComponent', internalInstance);
onSetChildren(debugID, childDebugIDs) {
emitEvent('onSetChildren', debugID, childDebugIDs);
},
onUpdateComponent(internalInstance) {
emitEvent('onUpdateComponent', internalInstance);
onSetOwner(debugID, ownerDebugID) {
emitEvent('onSetOwner', debugID, ownerDebugID);
},
onUnmountComponent(internalInstance) {
emitEvent('onUnmountComponent', internalInstance);
onSetText(debugID, text) {
emitEvent('onSetText', debugID, text);
},
onMountRootComponent(debugID) {
emitEvent('onMountRootComponent', debugID);
},
onMountComponent(debugID) {
emitEvent('onMountComponent', debugID);
},
onUpdateComponent(debugID) {
emitEvent('onUpdateComponent', debugID);
},
onUnmountComponent(debugID) {
emitEvent('onUnmountComponent', debugID);
},
onTestEvent() {
emitEvent('onTestEvent');
@@ -1,173 +0,0 @@
/**
* 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.
*
* @emails react-core
*/
'use strict';
describe('ReactDebugInstanceMap', function() {
var React;
var ReactDebugInstanceMap;
var ReactDOM;
beforeEach(function() {
jest.resetModuleRegistry();
React = require('React');
ReactDebugInstanceMap = require('ReactDebugInstanceMap');
ReactDOM = require('ReactDOM');
});
function createStubInstance() {
return { mountComponent: () => {} };
}
it('should register and unregister instances', function() {
var inst1 = createStubInstance();
var inst2 = createStubInstance();
expect(ReactDebugInstanceMap.isRegisteredInstance(inst1)).toBe(false);
expect(ReactDebugInstanceMap.isRegisteredInstance(inst2)).toBe(false);
ReactDebugInstanceMap.registerInstance(inst1);
expect(ReactDebugInstanceMap.isRegisteredInstance(inst1)).toBe(true);
expect(ReactDebugInstanceMap.isRegisteredInstance(inst2)).toBe(false);
ReactDebugInstanceMap.registerInstance(inst2);
expect(ReactDebugInstanceMap.isRegisteredInstance(inst1)).toBe(true);
expect(ReactDebugInstanceMap.isRegisteredInstance(inst2)).toBe(true);
ReactDebugInstanceMap.unregisterInstance(inst2);
expect(ReactDebugInstanceMap.isRegisteredInstance(inst1)).toBe(true);
expect(ReactDebugInstanceMap.isRegisteredInstance(inst2)).toBe(false);
ReactDebugInstanceMap.unregisterInstance(inst1);
expect(ReactDebugInstanceMap.isRegisteredInstance(inst1)).toBe(false);
expect(ReactDebugInstanceMap.isRegisteredInstance(inst2)).toBe(false);
});
it('should assign stable IDs', function() {
var inst1 = createStubInstance();
var inst2 = createStubInstance();
var inst1ID = ReactDebugInstanceMap.getIDForInstance(inst1);
var inst2ID = ReactDebugInstanceMap.getIDForInstance(inst2);
expect(typeof inst1ID).toBe('string');
expect(typeof inst2ID).toBe('string');
expect(inst1ID).not.toBe(inst2ID);
ReactDebugInstanceMap.registerInstance(inst1);
ReactDebugInstanceMap.registerInstance(inst2);
expect(ReactDebugInstanceMap.getIDForInstance(inst1)).toBe(inst1ID);
expect(ReactDebugInstanceMap.getIDForInstance(inst2)).toBe(inst2ID);
ReactDebugInstanceMap.unregisterInstance(inst1);
ReactDebugInstanceMap.unregisterInstance(inst2);
expect(ReactDebugInstanceMap.getIDForInstance(inst1)).toBe(inst1ID);
expect(ReactDebugInstanceMap.getIDForInstance(inst2)).toBe(inst2ID);
});
it('should retrieve registered instance by its ID', function() {
var inst1 = createStubInstance();
var inst2 = createStubInstance();
var inst1ID = ReactDebugInstanceMap.getIDForInstance(inst1);
var inst2ID = ReactDebugInstanceMap.getIDForInstance(inst2);
expect(ReactDebugInstanceMap.getInstanceByID(inst1ID)).toBe(null);
expect(ReactDebugInstanceMap.getInstanceByID(inst2ID)).toBe(null);
ReactDebugInstanceMap.registerInstance(inst1);
ReactDebugInstanceMap.registerInstance(inst2);
expect(ReactDebugInstanceMap.getInstanceByID(inst1ID)).toBe(inst1);
expect(ReactDebugInstanceMap.getInstanceByID(inst2ID)).toBe(inst2);
ReactDebugInstanceMap.unregisterInstance(inst1);
ReactDebugInstanceMap.unregisterInstance(inst2);
expect(ReactDebugInstanceMap.getInstanceByID(inst1ID)).toBe(null);
expect(ReactDebugInstanceMap.getInstanceByID(inst2ID)).toBe(null);
});
it('should warn when registering an instance twice', function() {
spyOn(console, 'error');
var inst = createStubInstance();
ReactDebugInstanceMap.registerInstance(inst);
expect(console.error.argsForCall.length).toBe(0);
ReactDebugInstanceMap.registerInstance(inst);
expect(console.error.argsForCall.length).toBe(1);
expect(console.error.argsForCall[0][0]).toContain(
'There is an internal error in the React developer tools integration. ' +
'A registered instance should not be registered again. ' +
'Please report this as a bug in React.'
);
ReactDebugInstanceMap.unregisterInstance(inst);
ReactDebugInstanceMap.registerInstance(inst);
expect(console.error.argsForCall.length).toBe(1);
});
it('should warn when unregistering an instance twice', function() {
spyOn(console, 'error');
var inst = createStubInstance();
ReactDebugInstanceMap.unregisterInstance(inst);
expect(console.error.argsForCall.length).toBe(1);
expect(console.error.argsForCall[0][0]).toContain(
'There is an internal error in the React developer tools integration. ' +
'An unregistered instance should not be unregistered again. ' +
'Please report this as a bug in React.'
);
ReactDebugInstanceMap.registerInstance(inst);
ReactDebugInstanceMap.unregisterInstance(inst);
expect(console.error.argsForCall.length).toBe(1);
ReactDebugInstanceMap.unregisterInstance(inst);
expect(console.error.argsForCall.length).toBe(2);
expect(console.error.argsForCall[1][0]).toContain(
'There is an internal error in the React developer tools integration. ' +
'An unregistered instance should not be unregistered again. ' +
'Please report this as a bug in React.'
);
});
it('should warn about anything than is not an internal instance', function() {
class Foo extends React.Component {
render() {
return <div />;
}
}
spyOn(console, 'error');
var warningCount = 0;
var div = document.createElement('div');
var publicInst = ReactDOM.render(<Foo />, div);
[false, null, undefined, {}, div, publicInst].forEach(falsyValue => {
ReactDebugInstanceMap.registerInstance(falsyValue);
warningCount++;
expect(ReactDebugInstanceMap.getIDForInstance(falsyValue)).toBe(null);
warningCount++;
expect(ReactDebugInstanceMap.isRegisteredInstance(falsyValue)).toBe(false);
warningCount++;
ReactDebugInstanceMap.unregisterInstance(falsyValue);
warningCount++;
});
expect(console.error.argsForCall.length).toBe(warningCount);
for (var i = 0; i < warningCount.length; i++) {
// Ideally we could check for the more detailed error message here
// but it depends on the input type and is meant for internal bugs
// anyway so I don't think it's worth complicating the test with it.
expect(console.error.argsForCall[i][0]).toContain(
'There is an internal error in the React developer tools integration.'
);
}
});
});
@@ -0,0 +1,148 @@
/**
* 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 rootIDs = [];
function updateTree(id, update) {
if (!tree[id]) {
tree[id] = {
parentID: null,
ownerID: null,
text: null,
childIDs: [],
displayName: 'Unknown',
isMounted: false,
};
}
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);
},
onMountComponent(id) {
updateTree(id, item => item.isMounted = true);
},
onMountRootComponent(id) {
rootIDs.push(id);
},
onUnmountComponent(id) {
updateTree(id, item => item.isMounted = false);
rootIDs = rootIDs.filter(rootID => rootID !== id);
},
purgeUnmountedComponents() {
Object.keys(tree)
.filter(id => !tree[id].isMounted)
.forEach(purgeDeep);
},
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;
},
getText(id) {
var item = tree[id];
return item ? item.text : null;
},
getRootIDs() {
return rootIDs;
},
getRegisteredIDs() {
return Object.keys(tree);
},
};
module.exports = ReactComponentTreeDevtool;
File diff suppressed because it is too large Load Diff
+10 -1
View File
@@ -333,6 +333,12 @@ var ReactMount = {
ReactBrowserEventEmitter.ensureScrollValueMonitoring();
var componentInstance = instantiateReactComponent(nextElement);
if (__DEV__) {
// Mute future events from the top level wrapper.
// It is an implementation detail that devtools should not know about.
componentInstance._debugID = 0;
}
// The initial render is synchronous but any updates that happen during
// rendering, in componentWillMount or componentDidMount, will be batched
// according to the current batching strategy.
@@ -349,7 +355,10 @@ var ReactMount = {
instancesByReactRootID[wrapperID] = componentInstance;
if (__DEV__) {
ReactInstrumentation.debugTool.onMountRootComponent(componentInstance);
// The instance here is TopLevelWrapper so we report mount for its child.
ReactInstrumentation.debugTool.onMountRootComponent(
componentInstance._renderedComponent._debugID
);
}
return componentInstance;
@@ -13,7 +13,9 @@
var ReactDOMContainerInfo = require('ReactDOMContainerInfo');
var ReactDefaultBatchingStrategy = require('ReactDefaultBatchingStrategy');
var ReactElement = require('ReactElement');
var ReactInstrumentation = require('ReactInstrumentation');
var ReactMarkupChecksum = require('ReactMarkupChecksum');
var ReactReconciler = require('ReactReconciler');
var ReactServerBatchingStrategy = require('ReactServerBatchingStrategy');
var ReactServerRenderingTransaction =
require('ReactServerRenderingTransaction');
@@ -36,12 +38,18 @@ function renderToStringImpl(element, makeStaticMarkup) {
return transaction.perform(function() {
var componentInstance = instantiateReactComponent(element);
var markup = componentInstance.mountComponent(
var markup = ReactReconciler.mountComponent(
componentInstance,
transaction,
null,
ReactDOMContainerInfo(),
emptyObject
);
if (__DEV__) {
ReactInstrumentation.debugTool.onUnmountComponent(
componentInstance._debugID
);
}
if (!makeStaticMarkup) {
markup = ReactMarkupChecksum.addChecksumToMarkup(markup);
}
@@ -32,9 +32,11 @@ var ReactDOMInput = require('ReactDOMInput');
var ReactDOMOption = require('ReactDOMOption');
var ReactDOMSelect = require('ReactDOMSelect');
var ReactDOMTextarea = require('ReactDOMTextarea');
var ReactInstrumentation = require('ReactInstrumentation');
var ReactMultiChild = require('ReactMultiChild');
var ReactPerf = require('ReactPerf');
var emptyFunction = require('emptyFunction');
var escapeTextContentForBrowser = require('escapeTextContentForBrowser');
var invariant = require('invariant');
var isEventSupported = require('isEventSupported');
@@ -245,6 +247,19 @@ function optionPostMount() {
ReactDOMOption.postMountWrapper(inst);
}
var setContentChildForInstrumentation = emptyFunction;
if (__DEV__) {
setContentChildForInstrumentation = function(contentToUse) {
var debugID = this._debugID;
var contentDebugID = debugID + '#text';
this._contentDebugID = contentDebugID;
ReactInstrumentation.debugTool.onSetDisplayName(contentDebugID, '#text');
ReactInstrumentation.debugTool.onSetText(contentDebugID, '' + contentToUse);
ReactInstrumentation.debugTool.onMountComponent(contentDebugID);
ReactInstrumentation.debugTool.onSetChildren(debugID, [contentDebugID]);
};
}
// There are so many media events, it makes sense to just
// maintain a list rather than create a `trapBubbledEvent` for each
var mediaEvents = {
@@ -452,6 +467,7 @@ function ReactDOMComponent(element) {
this._flags = 0;
if (__DEV__) {
this._ancestorInfo = null;
this._contentDebugID = null;
}
}
@@ -717,6 +733,9 @@ ReactDOMComponent.Mixin = {
if (contentToUse != null) {
// TODO: Validate that text is allowed as a child of this node
ret = escapeTextContentForBrowser(contentToUse);
if (__DEV__) {
setContentChildForInstrumentation.call(this, contentToUse);
}
} else if (childrenToUse != null) {
var mountImages = this.mountChildren(
childrenToUse,
@@ -756,6 +775,9 @@ ReactDOMComponent.Mixin = {
var childrenToUse = contentToUse != null ? null : props.children;
if (contentToUse != null) {
// TODO: Validate that text is allowed as a child of this node
if (__DEV__) {
setContentChildForInstrumentation.call(this, contentToUse);
}
DOMLazyTree.queueText(lazyTree, contentToUse);
} else if (childrenToUse != null) {
var mountImages = this.mountChildren(
@@ -1003,17 +1025,34 @@ ReactDOMComponent.Mixin = {
this.updateChildren(null, transaction, context);
} else if (lastHasContentOrHtml && !nextHasContentOrHtml) {
this.updateTextContent('');
if (__DEV__) {
ReactInstrumentation.debugTool.onSetChildren(this._debugID, []);
}
}
if (nextContent != null) {
if (lastContent !== nextContent) {
this.updateTextContent('' + nextContent);
if (__DEV__) {
this._contentDebugID = this._debugID + '#text';
setContentChildForInstrumentation.call(this, nextContent);
}
}
} else if (nextHtml != null) {
if (lastHtml !== nextHtml) {
this.updateMarkup('' + nextHtml);
}
if (__DEV__) {
ReactInstrumentation.debugTool.onSetChildren(this._debugID, []);
}
} else if (nextChildren != null) {
if (__DEV__) {
if (this._contentDebugID) {
ReactInstrumentation.debugTool.onUnmountComponent(this._contentDebugID);
this._contentDebugID = null;
}
}
this.updateChildren(nextChildren, transaction, context);
}
},
@@ -1075,6 +1114,11 @@ ReactDOMComponent.Mixin = {
this._rootNodeID = null;
this._domID = null;
this._wrapperState = null;
if (this._contentDebugID) {
ReactInstrumentation.debugTool.onUnmountComponent(this._contentDebugID);
this._contentDebugID = null;
}
},
getPublicInstance: function() {
@@ -14,6 +14,7 @@
var DOMChildrenOperations = require('DOMChildrenOperations');
var DOMLazyTree = require('DOMLazyTree');
var ReactDOMComponentTree = require('ReactDOMComponentTree');
var ReactInstrumentation = require('ReactInstrumentation');
var ReactPerf = require('ReactPerf');
var escapeTextContentForBrowser = require('escapeTextContentForBrowser');
@@ -67,6 +68,8 @@ Object.assign(ReactDOMTextComponent.prototype, {
context
) {
if (__DEV__) {
ReactInstrumentation.debugTool.onSetText(this._debugID, this._stringText);
var parentInfo;
if (nativeParent != null) {
parentInfo = nativeParent._ancestorInfo;
@@ -140,6 +143,10 @@ Object.assign(ReactDOMTextComponent.prototype, {
commentNodes[1],
nextStringText
);
if (__DEV__) {
ReactInstrumentation.debugTool.onSetText(this._debugID, nextStringText);
}
}
}
},
@@ -386,6 +386,17 @@ var ReactCompositeComponentMixin = {
this._processChildContext(context)
);
if (__DEV__) {
if (this._debugID !== 0) {
ReactInstrumentation.debugTool.onSetChildren(
this._debugID,
this._renderedComponent._debugID !== 0 ?
[this._renderedComponent._debugID] :
[]
);
}
}
return markup;
},
@@ -853,6 +864,7 @@ var ReactCompositeComponentMixin = {
this._renderedComponent = this._instantiateReactComponent(
nextRenderedElement
);
var nextMarkup = ReactReconciler.mountComponent(
this._renderedComponent,
transaction,
@@ -860,6 +872,18 @@ var ReactCompositeComponentMixin = {
this._nativeContainerInfo,
this._processChildContext(context)
);
if (__DEV__) {
if (this._debugID !== 0) {
ReactInstrumentation.debugTool.onSetChildren(
this._debugID,
this._renderedComponent._debugID !== 0 ?
[this._renderedComponent._debugID] :
[]
);
}
}
this._replaceNodeWithMarkup(oldNativeNode, nextMarkup);
}
},
@@ -12,12 +12,14 @@
'use strict';
var ReactComponentEnvironment = require('ReactComponentEnvironment');
var ReactInstrumentation = require('ReactInstrumentation');
var ReactMultiChildUpdateTypes = require('ReactMultiChildUpdateTypes');
var ReactCurrentOwner = require('ReactCurrentOwner');
var ReactReconciler = require('ReactReconciler');
var ReactChildReconciler = require('ReactChildReconciler');
var emptyFunction = require('emptyFunction');
var flattenChildren = require('flattenChildren');
var invariant = require('invariant');
@@ -137,6 +139,16 @@ function processQueue(inst, updateQueue) {
);
}
var setChildrenForInstrumentation = emptyFunction;
if (__DEV__) {
setChildrenForInstrumentation = function(children) {
ReactInstrumentation.debugTool.onSetChildren(
this._debugID,
children ? Object.keys(children).map(key => children[key]._debugID) : []
);
};
}
/**
* ReactMultiChild are capable of reconciling multiple children.
*
@@ -214,6 +226,7 @@ var ReactMultiChild = {
nestedChildren, transaction, context
);
this._renderedChildren = children;
var mountImages = [];
var index = 0;
for (var name in children) {
@@ -230,6 +243,11 @@ var ReactMultiChild = {
mountImages.push(mountImage);
}
}
if (__DEV__) {
setChildrenForInstrumentation.call(this, children);
}
return mountImages;
},
@@ -357,6 +375,10 @@ var ReactMultiChild = {
processQueue(this, updates);
}
this._renderedChildren = nextChildren;
if (__DEV__) {
setChildrenForInstrumentation.call(this, nextChildren);
}
},
/**
@@ -53,7 +53,9 @@ var ReactReconciler = {
transaction.getReactMountReady().enqueue(attachRefs, internalInstance);
}
if (__DEV__) {
ReactInstrumentation.debugTool.onMountComponent(internalInstance);
if (internalInstance._debugID !== 0) {
ReactInstrumentation.debugTool.onMountComponent(internalInstance._debugID);
}
}
return markup;
},
@@ -76,7 +78,9 @@ var ReactReconciler = {
ReactRef.detachRefs(internalInstance, internalInstance._currentElement);
internalInstance.unmountComponent(safely);
if (__DEV__) {
ReactInstrumentation.debugTool.onUnmountComponent(internalInstance);
if (internalInstance._debugID !== 0) {
ReactInstrumentation.debugTool.onUnmountComponent(internalInstance._debugID);
}
}
},
@@ -128,7 +132,9 @@ var ReactReconciler = {
}
if (__DEV__) {
ReactInstrumentation.debugTool.onUpdateComponent(internalInstance);
if (internalInstance._debugID !== 0) {
ReactInstrumentation.debugTool.onUpdateComponent(internalInstance._debugID);
}
}
},
@@ -145,7 +151,9 @@ var ReactReconciler = {
) {
internalInstance.performUpdateIfNecessary(transaction);
if (__DEV__) {
ReactInstrumentation.debugTool.onUpdateComponent(internalInstance);
if (internalInstance._debugID !== 0) {
ReactInstrumentation.debugTool.onUpdateComponent(internalInstance._debugID);
}
}
},
@@ -14,6 +14,7 @@
var ReactCompositeComponent = require('ReactCompositeComponent');
var ReactEmptyComponent = require('ReactEmptyComponent');
var ReactNativeComponent = require('ReactNativeComponent');
var ReactInstrumentation = require('ReactInstrumentation');
var invariant = require('invariant');
var warning = require('warning');
@@ -40,6 +41,21 @@ function getDeclarationErrorAddendum(owner) {
return '';
}
function getDisplayName(instance) {
var element = instance._currentElement;
if (element == null) {
return '#empty';
} else if (typeof element === 'string' || typeof element === 'number') {
return '#text';
} else if (typeof element.type === 'string') {
return element.type;
} else if (instance.getName) {
return instance.getName() || 'Unknown';
} else {
return element.type.displayName || element.type.name || 'Unknown';
}
}
/**
* Check if the type reference is a known internal type. I.e. not a user
* provided composite type.
@@ -56,6 +72,8 @@ function isInternalComponentType(type) {
);
}
var nextDebugID = 1;
/**
* Given a ReactNode, create an instance that will actually be mounted.
*
@@ -66,7 +84,8 @@ function isInternalComponentType(type) {
function instantiateReactComponent(node) {
var instance;
if (node === null || node === false) {
var isEmpty = node === null || node === false;
if (isEmpty) {
instance = ReactEmptyComponent.create(instantiateReactComponent);
} else if (typeof node === 'object') {
var element = node;
@@ -121,6 +140,18 @@ function instantiateReactComponent(node) {
instance._warnedAboutRefsInRender = false;
}
if (__DEV__) {
var debugID = isEmpty ? 0 : nextDebugID++;
instance._debugID = debugID;
var displayName = getDisplayName(instance);
ReactInstrumentation.debugTool.onSetDisplayName(debugID, displayName);
var owner = node && node._owner;
if (owner) {
ReactInstrumentation.debugTool.onSetOwner(debugID, owner._debugID);
}
}
// Internal instances should fully constructed at this point, so they should
// not get any new fields added to them at this point.
if (__DEV__) {