diff --git a/src/isomorphic/ReactDebugTool.js b/src/isomorphic/ReactDebugTool.js
index 8fbc0341f8..005378b76d 100644
--- a/src/isomorphic/ReactDebugTool.js
+++ b/src/isomorphic/ReactDebugTool.js
@@ -90,6 +90,10 @@ function resetMeasurements() {
}
}
+function checkDebugID(debugID) {
+ warning(debugID, 'ReactDebugTool: debugID may not be empty.');
+}
+
var ReactDebugTool = {
addDevtool(devtool) {
eventHandlers.push(devtool);
@@ -143,6 +147,7 @@ var ReactDebugTool = {
emitEvent('onEndFlush');
},
onBeginLifeCycleTimer(debugID, timerType) {
+ checkDebugID(debugID);
emitEvent('onBeginLifeCycleTimer', debugID, timerType);
if (__DEV__) {
if (isProfiling && currentFlushNesting > 0) {
@@ -162,6 +167,7 @@ var ReactDebugTool = {
}
},
onEndLifeCycleTimer(debugID, timerType) {
+ checkDebugID(debugID);
if (__DEV__) {
if (isProfiling && currentFlushNesting > 0) {
warning(
@@ -186,9 +192,11 @@ var ReactDebugTool = {
emitEvent('onEndLifeCycleTimer', debugID, timerType);
},
onBeginReconcilerTimer(debugID, timerType) {
+ checkDebugID(debugID);
emitEvent('onBeginReconcilerTimer', debugID, timerType);
},
onEndReconcilerTimer(debugID, timerType) {
+ checkDebugID(debugID);
emitEvent('onEndReconcilerTimer', debugID, timerType);
},
onBeginProcessingChildContext() {
@@ -198,33 +206,42 @@ var ReactDebugTool = {
emitEvent('onEndProcessingChildContext');
},
onNativeOperation(debugID, type, payload) {
+ checkDebugID(debugID);
emitEvent('onNativeOperation', debugID, type, payload);
},
onSetState() {
emitEvent('onSetState');
},
onSetDisplayName(debugID, displayName) {
+ checkDebugID(debugID);
emitEvent('onSetDisplayName', debugID, displayName);
},
onSetChildren(debugID, childDebugIDs) {
+ checkDebugID(debugID);
emitEvent('onSetChildren', debugID, childDebugIDs);
},
onSetOwner(debugID, ownerDebugID) {
+ checkDebugID(debugID);
emitEvent('onSetOwner', debugID, ownerDebugID);
},
onSetText(debugID, text) {
+ checkDebugID(debugID);
emitEvent('onSetText', debugID, text);
},
onMountRootComponent(debugID) {
+ checkDebugID(debugID);
emitEvent('onMountRootComponent', debugID);
},
onMountComponent(debugID) {
+ checkDebugID(debugID);
emitEvent('onMountComponent', debugID);
},
onUpdateComponent(debugID) {
+ checkDebugID(debugID);
emitEvent('onUpdateComponent', debugID);
},
onUnmountComponent(debugID) {
+ checkDebugID(debugID);
emitEvent('onUnmountComponent', debugID);
},
onTestEvent() {
diff --git a/src/isomorphic/devtools/__tests__/ReactNativeOperationHistoryDevtool-test.js b/src/isomorphic/devtools/__tests__/ReactNativeOperationHistoryDevtool-test.js
index 879f203a35..a5f1c5101e 100644
--- a/src/isomorphic/devtools/__tests__/ReactNativeOperationHistoryDevtool-test.js
+++ b/src/isomorphic/devtools/__tests__/ReactNativeOperationHistoryDevtool-test.js
@@ -72,13 +72,17 @@ describe('ReactNativeOperationHistoryDevtool', () => {
var node = document.createElement('div');
ReactDOM.render(, node);
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
- assertHistoryMatches([{
- instanceID: inst._debugID,
- type: 'mount',
- payload: ReactDOMFeatureFlags.useCreateElement ?
- '#comment' :
- '',
- }]);
+
+ if (ReactDOMFeatureFlags.useCreateElement) {
+ // Empty DOM components should be invisible to devtools.
+ assertHistoryMatches([]);
+ } else {
+ assertHistoryMatches([{
+ instanceID: inst._debugID,
+ type: 'mount',
+ payload: '',
+ }]);
+ }
});
it('gets recorded when a native is mounted deeply instead of null', () => {
@@ -95,6 +99,9 @@ describe('ReactNativeOperationHistoryDevtool', () => {
element = ;
ReactDOM.render(, node);
var inst = ReactDOMComponentTree.getInstanceFromNode(node.firstChild);
+
+ // Since empty components should be invisible to devtools,
+ // we record a "mount" event rather than a "replace with".
assertHistoryMatches([{
instanceID: inst._debugID,
type: 'mount',
diff --git a/src/renderers/dom/client/ReactMount.js b/src/renderers/dom/client/ReactMount.js
index eebc607c13..6ec60a21fa 100644
--- a/src/renderers/dom/client/ReactMount.js
+++ b/src/renderers/dom/client/ReactMount.js
@@ -701,11 +701,14 @@ var ReactMount = {
}
if (__DEV__) {
- ReactInstrumentation.debugTool.onNativeOperation(
- ReactDOMComponentTree.getInstanceFromNode(container.firstChild)._debugID,
- 'mount',
- markup.toString()
- );
+ var nativeNode = ReactDOMComponentTree.getInstanceFromNode(container.firstChild);
+ if (nativeNode._debugID !== 0) {
+ ReactInstrumentation.debugTool.onNativeOperation(
+ nativeNode._debugID,
+ 'mount',
+ markup.toString()
+ );
+ }
}
},
};
diff --git a/src/renderers/dom/client/__tests__/ReactDOMIDOperations-test.js b/src/renderers/dom/client/__tests__/ReactDOMIDOperations-test.js
index 1e5c05a6aa..431a21e0f2 100644
--- a/src/renderers/dom/client/__tests__/ReactDOMIDOperations-test.js
+++ b/src/renderers/dom/client/__tests__/ReactDOMIDOperations-test.js
@@ -18,7 +18,7 @@ describe('ReactDOMIDOperations', function() {
it('should update innerHTML and preserve whitespace', function() {
var stubNode = document.createElement('div');
- var stubInstance = {};
+ var stubInstance = {_debugID: 1};
ReactDOMComponentTree.precacheNode(stubInstance, stubNode);
var html = '\n \t \n testContent \t \n \t';
diff --git a/src/renderers/dom/client/utils/DOMChildrenOperations.js b/src/renderers/dom/client/utils/DOMChildrenOperations.js
index f213c9e783..4f0da4b7cc 100644
--- a/src/renderers/dom/client/utils/DOMChildrenOperations.js
+++ b/src/renderers/dom/client/utils/DOMChildrenOperations.js
@@ -142,11 +142,14 @@ if (__DEV__) {
markup.toString()
);
} else {
- ReactInstrumentation.debugTool.onNativeOperation(
- ReactDOMComponentTree.getInstanceFromNode(markup.node)._debugID,
- 'mount',
- markup.toString()
- );
+ var nextInstance = ReactDOMComponentTree.getInstanceFromNode(markup.node);
+ if (nextInstance._debugID !== 0) {
+ ReactInstrumentation.debugTool.onNativeOperation(
+ nextInstance._debugID,
+ 'mount',
+ markup.toString()
+ );
+ }
}
};
}
diff --git a/src/renderers/dom/shared/__tests__/DOMPropertyOperations-test.js b/src/renderers/dom/shared/__tests__/DOMPropertyOperations-test.js
index 4f8f82cb10..4200d37678 100644
--- a/src/renderers/dom/shared/__tests__/DOMPropertyOperations-test.js
+++ b/src/renderers/dom/shared/__tests__/DOMPropertyOperations-test.js
@@ -174,10 +174,12 @@ describe('DOMPropertyOperations', function() {
describe('setValueForProperty', function() {
var stubNode;
+ var stubInstance;
beforeEach(function() {
stubNode = document.createElement('div');
- ReactDOMComponentTree.precacheNode({}, stubNode);
+ stubInstance = {_debugID: 1};
+ ReactDOMComponentTree.precacheNode(stubInstance, stubNode);
});
it('should set values as properties by default', function() {
@@ -226,7 +228,7 @@ describe('DOMPropertyOperations', function() {
it('should not remove empty attributes for special properties', function() {
stubNode = document.createElement('input');
- ReactDOMComponentTree.precacheNode({}, stubNode);
+ ReactDOMComponentTree.precacheNode(stubInstance, stubNode);
DOMPropertyOperations.setValueForProperty(stubNode, 'value', '');
// JSDOM does not behave correctly for attributes/properties
@@ -348,10 +350,12 @@ describe('DOMPropertyOperations', function() {
describe('deleteValueForProperty', function() {
var stubNode;
+ var stubInstance;
beforeEach(function() {
stubNode = document.createElement('div');
- ReactDOMComponentTree.precacheNode({}, stubNode);
+ stubInstance = {_debugID: 1};
+ ReactDOMComponentTree.precacheNode(stubInstance, stubNode);
});
it('should remove attributes for normal properties', function() {
@@ -367,7 +371,7 @@ describe('DOMPropertyOperations', function() {
it('should not remove attributes for special properties', function() {
stubNode = document.createElement('input');
- ReactDOMComponentTree.precacheNode({}, stubNode);
+ ReactDOMComponentTree.precacheNode(stubInstance, stubNode);
stubNode.setAttribute('value', 'foo');
@@ -379,7 +383,7 @@ describe('DOMPropertyOperations', function() {
it('should not leave all options selected when deleting multiple', function() {
stubNode = document.createElement('select');
- ReactDOMComponentTree.precacheNode({}, stubNode);
+ ReactDOMComponentTree.precacheNode(stubInstance, stubNode);
stubNode.multiple = true;
stubNode.appendChild(document.createElement('option'));
diff --git a/src/renderers/shared/stack/reconciler/instantiateReactComponent.js b/src/renderers/shared/stack/reconciler/instantiateReactComponent.js
index 968c2991f7..0632a3c4d5 100644
--- a/src/renderers/shared/stack/reconciler/instantiateReactComponent.js
+++ b/src/renderers/shared/stack/reconciler/instantiateReactComponent.js
@@ -139,11 +139,13 @@ function instantiateReactComponent(node) {
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);
+ if (debugID !== 0) {
+ var displayName = getDisplayName(instance);
+ ReactInstrumentation.debugTool.onSetDisplayName(debugID, displayName);
+ var owner = node && node._owner;
+ if (owner) {
+ ReactInstrumentation.debugTool.onSetOwner(debugID, owner._debugID);
+ }
}
}
diff --git a/src/test/ReactTestUtils.js b/src/test/ReactTestUtils.js
index 9937bf06e7..a9093a49a2 100644
--- a/src/test/ReactTestUtils.js
+++ b/src/test/ReactTestUtils.js
@@ -376,9 +376,12 @@ ReactShallowRenderer.prototype.getMountedInstance = function() {
return this._instance ? this._instance._instance : null;
};
+var nextDebugID = 0;
+
var NoopInternalComponent = function(element) {
this._renderedOutput = element;
this._currentElement = element;
+ this._debugID = nextDebugID++;
};
NoopInternalComponent.prototype = {
@@ -404,6 +407,7 @@ NoopInternalComponent.prototype = {
};
var ShallowComponentWrapper = function(element) {
+ this._debugID = nextDebugID++;
this.construct(element);
};
Object.assign(