mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Pass shouldHaveDebugID flag to instantiateComponent (#7193)
* Add failing tests for #7187 and #7190 * Pass shouldHaveDebugID flag to instantiateComponent This allows us to remove a hack that was added in #6770 and caused #7187 and #7190. * Move logic for choosing whether to use debugID outside instantiate
This commit is contained in:
1 parent
2c93a41580
commit
d2ff462b79
11 files changed
+162
-140
No files matched your search
@@ -32,10 +32,6 @@ function updateTree(id, update) {
|
||||
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]);
|
||||
}
|
||||
@@ -148,7 +144,6 @@ var ReactComponentTreeDevtool = {
|
||||
|
||||
onMountComponent(id) {
|
||||
updateTree(id, item => item.isMounted = true);
|
||||
delete unmountedIDs[id];
|
||||
},
|
||||
|
||||
onMountRootComponent(id) {
|
||||
|
||||
@@ -342,13 +342,7 @@ 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;
|
||||
}
|
||||
var componentInstance = instantiateReactComponent(nextElement, false);
|
||||
|
||||
// The initial render is synchronous but any updates that happen during
|
||||
// rendering, in componentWillMount or componentDidMount, will be batched
|
||||
|
||||
@@ -40,7 +40,7 @@ function renderToStringImpl(element, makeStaticMarkup) {
|
||||
if (__DEV__) {
|
||||
ReactInstrumentation.debugTool.onBeginFlush();
|
||||
}
|
||||
var componentInstance = instantiateReactComponent(element);
|
||||
var componentInstance = instantiateReactComponent(element, true);
|
||||
var markup = ReactReconciler.mountComponent(
|
||||
componentInstance,
|
||||
transaction,
|
||||
|
||||
@@ -135,17 +135,11 @@ var ReactNativeMount = {
|
||||
|
||||
ReactNativeTagHandles.assertRootTag(containerTag);
|
||||
|
||||
var instance = instantiateReactComponent(nextWrappedElement);
|
||||
var instance = instantiateReactComponent(nextWrappedElement, false);
|
||||
ReactNativeMount._instancesByContainerID[containerTag] = instance;
|
||||
|
||||
if (__DEV__) {
|
||||
// Mute future events from the top level wrapper.
|
||||
// It is an implementation detail that devtools should not know about.
|
||||
instance._debugID = 0;
|
||||
|
||||
if (__DEV__) {
|
||||
ReactInstrumentation.debugTool.onBeginFlush();
|
||||
}
|
||||
ReactInstrumentation.debugTool.onBeginFlush();
|
||||
}
|
||||
|
||||
// The initial render is synchronous but any updates that happen during
|
||||
|
||||
@@ -263,6 +263,7 @@ var ReactDebugTool = {
|
||||
},
|
||||
onSetChildren(debugID, childDebugIDs) {
|
||||
checkDebugID(debugID);
|
||||
childDebugIDs.forEach(checkDebugID);
|
||||
emitEvent('onSetChildren', debugID, childDebugIDs);
|
||||
},
|
||||
onSetOwner(debugID, ownerDebugID) {
|
||||
|
||||
@@ -1716,103 +1716,137 @@ describe('ReactComponentTreeDevtool', () => {
|
||||
expect(ReactComponentTreeTestUtils.getRegisteredDisplayNames()).toEqual([]);
|
||||
});
|
||||
|
||||
it('creates current stack addenda', () => {
|
||||
function getAddendum(element) {
|
||||
var addendum = ReactComponentTreeDevtool.getCurrentStackAddendum(element);
|
||||
return addendum.replace(/\(at .+?:\d+\)/g, '(at **)');
|
||||
}
|
||||
|
||||
var Anon = React.createClass({displayName: null, render: () => null});
|
||||
var Orange = React.createClass({render: () => null});
|
||||
|
||||
expect(getAddendum()).toBe(
|
||||
''
|
||||
);
|
||||
expect(getAddendum(<div />)).toBe(
|
||||
'\n in div (at **)'
|
||||
);
|
||||
expect(getAddendum(<Anon />)).toBe(
|
||||
'\n in Unknown (at **)'
|
||||
);
|
||||
expect(getAddendum(<Orange />)).toBe(
|
||||
'\n in Orange (at **)'
|
||||
);
|
||||
expect(getAddendum(React.createElement(Orange))).toBe(
|
||||
'\n in Orange'
|
||||
);
|
||||
|
||||
var renders = 0;
|
||||
var rOwnedByQ;
|
||||
|
||||
function Q() {
|
||||
return (rOwnedByQ = React.createElement(R));
|
||||
}
|
||||
function R() {
|
||||
return <div><S /></div>;
|
||||
}
|
||||
class S extends React.Component {
|
||||
componentDidMount() {
|
||||
// Check that the parent path is still fetched when only S itself is on
|
||||
// the stack.
|
||||
this.forceUpdate();
|
||||
describe('stack addenda', () => {
|
||||
it('gets created', () => {
|
||||
function getAddendum(element) {
|
||||
var addendum = ReactComponentTreeDevtool.getCurrentStackAddendum(element);
|
||||
return addendum.replace(/\(at .+?:\d+\)/g, '(at **)');
|
||||
}
|
||||
render() {
|
||||
expect(getAddendum()).toBe(
|
||||
'\n in S (at **)' +
|
||||
'\n in div (at **)' +
|
||||
'\n in R (created by Q)' +
|
||||
'\n in Q (at **)'
|
||||
);
|
||||
expect(getAddendum(<span />)).toBe(
|
||||
'\n in span (at **)' +
|
||||
'\n in S (at **)' +
|
||||
'\n in div (at **)' +
|
||||
'\n in R (created by Q)' +
|
||||
'\n in Q (at **)'
|
||||
);
|
||||
expect(getAddendum(React.createElement('span'))).toBe(
|
||||
'\n in span (created by S)' +
|
||||
'\n in S (at **)' +
|
||||
'\n in div (at **)' +
|
||||
'\n in R (created by Q)' +
|
||||
'\n in Q (at **)'
|
||||
);
|
||||
renders++;
|
||||
return null;
|
||||
|
||||
var Anon = React.createClass({displayName: null, render: () => null});
|
||||
var Orange = React.createClass({render: () => null});
|
||||
|
||||
expect(getAddendum()).toBe(
|
||||
''
|
||||
);
|
||||
expect(getAddendum(<div />)).toBe(
|
||||
'\n in div (at **)'
|
||||
);
|
||||
expect(getAddendum(<Anon />)).toBe(
|
||||
'\n in Unknown (at **)'
|
||||
);
|
||||
expect(getAddendum(<Orange />)).toBe(
|
||||
'\n in Orange (at **)'
|
||||
);
|
||||
expect(getAddendum(React.createElement(Orange))).toBe(
|
||||
'\n in Orange'
|
||||
);
|
||||
|
||||
var renders = 0;
|
||||
var rOwnedByQ;
|
||||
|
||||
function Q() {
|
||||
return (rOwnedByQ = React.createElement(R));
|
||||
}
|
||||
}
|
||||
ReactDOM.render(<Q />, document.createElement('div'));
|
||||
expect(renders).toBe(2);
|
||||
|
||||
// Make sure owner is fetched for the top element too.
|
||||
expect(getAddendum(rOwnedByQ)).toBe(
|
||||
'\n in R (created by Q)'
|
||||
);
|
||||
});
|
||||
|
||||
it('creates stack addenda by ID', () => {
|
||||
function getAddendum(id) {
|
||||
var addendum = ReactComponentTreeDevtool.getStackAddendumByID(id);
|
||||
return addendum.replace(/\(at .+?:\d+\)/g, '(at **)');
|
||||
}
|
||||
|
||||
class Q extends React.Component {
|
||||
render() {
|
||||
return null;
|
||||
function R() {
|
||||
return <div><S /></div>;
|
||||
}
|
||||
}
|
||||
class S extends React.Component {
|
||||
componentDidMount() {
|
||||
// Check that the parent path is still fetched when only S itself is on
|
||||
// the stack.
|
||||
this.forceUpdate();
|
||||
}
|
||||
render() {
|
||||
expect(getAddendum()).toBe(
|
||||
'\n in S (at **)' +
|
||||
'\n in div (at **)' +
|
||||
'\n in R (created by Q)' +
|
||||
'\n in Q (at **)'
|
||||
);
|
||||
expect(getAddendum(<span />)).toBe(
|
||||
'\n in span (at **)' +
|
||||
'\n in S (at **)' +
|
||||
'\n in div (at **)' +
|
||||
'\n in R (created by Q)' +
|
||||
'\n in Q (at **)'
|
||||
);
|
||||
expect(getAddendum(React.createElement('span'))).toBe(
|
||||
'\n in span (created by S)' +
|
||||
'\n in S (at **)' +
|
||||
'\n in div (at **)' +
|
||||
'\n in R (created by Q)' +
|
||||
'\n in Q (at **)'
|
||||
);
|
||||
renders++;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
ReactDOM.render(<Q />, document.createElement('div'));
|
||||
expect(renders).toBe(2);
|
||||
|
||||
var q = ReactDOM.render(<Q />, document.createElement('div'));
|
||||
expect(getAddendum(ReactInstanceMap.get(q)._debugID)).toBe(
|
||||
'\n in Q (at **)'
|
||||
);
|
||||
// Make sure owner is fetched for the top element too.
|
||||
expect(getAddendum(rOwnedByQ)).toBe(
|
||||
'\n in R (created by Q)'
|
||||
);
|
||||
});
|
||||
|
||||
spyOn(console, 'error');
|
||||
getAddendum(-17);
|
||||
expect(console.error.calls.count()).toBe(1);
|
||||
expect(console.error.calls.argsFor(0)[0]).toBe(
|
||||
'Warning: ReactComponentTreeDevtool: Missing React element for debugID ' +
|
||||
'-17 when building stack'
|
||||
);
|
||||
});
|
||||
it('can be retrieved by ID', () => {
|
||||
function getAddendum(id) {
|
||||
var addendum = ReactComponentTreeDevtool.getStackAddendumByID(id);
|
||||
return addendum.replace(/\(at .+?:\d+\)/g, '(at **)');
|
||||
}
|
||||
|
||||
class Q extends React.Component {
|
||||
render() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
var q = ReactDOM.render(<Q />, document.createElement('div'));
|
||||
expect(getAddendum(ReactInstanceMap.get(q)._debugID)).toBe(
|
||||
'\n in Q (at **)'
|
||||
);
|
||||
|
||||
spyOn(console, 'error');
|
||||
getAddendum(-17);
|
||||
expect(console.error.calls.count()).toBe(1);
|
||||
expect(console.error.calls.argsFor(0)[0]).toBe(
|
||||
'Warning: ReactComponentTreeDevtool: Missing React element for ' +
|
||||
'debugID -17 when building stack'
|
||||
);
|
||||
});
|
||||
|
||||
it('is created during mounting', () => {
|
||||
// https://github.com/facebook/react/issues/7187
|
||||
var el = document.createElement('div');
|
||||
var portalEl = document.createElement('div');
|
||||
class Foo extends React.Component {
|
||||
componentWillMount() {
|
||||
ReactDOM.render(<div />, portalEl);
|
||||
}
|
||||
render() {
|
||||
return <div><div /></div>;
|
||||
}
|
||||
}
|
||||
ReactDOM.render(<Foo />, el);
|
||||
});
|
||||
|
||||
it('is created when calling renderToString during render', () => {
|
||||
// https://github.com/facebook/react/issues/7190
|
||||
var el = document.createElement('div');
|
||||
class Foo extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<div>
|
||||
{ReactDOMServer.renderToString(<div />)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
ReactDOM.render(<Foo />, el);
|
||||
});
|
||||
})
|
||||
});
|
||||
@@ -34,7 +34,7 @@ function instantiateChild(childInstances, child, name, selfDebugID) {
|
||||
);
|
||||
}
|
||||
if (child != null && keyUnique) {
|
||||
childInstances[name] = instantiateReactComponent(child);
|
||||
childInstances[name] = instantiateReactComponent(child, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,7 +128,7 @@ var ReactChildReconciler = {
|
||||
ReactReconciler.unmountComponent(prevChild, false);
|
||||
}
|
||||
// The child must be instantiated before it's mounted.
|
||||
var nextChildInstance = instantiateReactComponent(nextElement);
|
||||
var nextChildInstance = instantiateReactComponent(nextElement, true);
|
||||
nextChildren[name] = nextChildInstance;
|
||||
// Creating mount image now ensures refs are resolved in right order
|
||||
// (see https://github.com/facebook/react/pull/7101 for explanation).
|
||||
|
||||
@@ -473,9 +473,11 @@ var ReactCompositeComponentMixin = {
|
||||
renderedElement = this._renderValidatedComponent();
|
||||
}
|
||||
|
||||
this._renderedNodeType = ReactNodeTypes.getType(renderedElement);
|
||||
var nodeType = ReactNodeTypes.getType(renderedElement);
|
||||
this._renderedNodeType = nodeType;
|
||||
var child = this._instantiateReactComponent(
|
||||
renderedElement
|
||||
renderedElement,
|
||||
nodeType !== ReactNodeTypes.EMPTY /* shouldHaveDebugID */
|
||||
);
|
||||
this._renderedComponent = child;
|
||||
if (__DEV__) {
|
||||
@@ -990,9 +992,11 @@ var ReactCompositeComponentMixin = {
|
||||
var oldHostNode = ReactReconciler.getHostNode(prevComponentInstance);
|
||||
ReactReconciler.unmountComponent(prevComponentInstance, false);
|
||||
|
||||
this._renderedNodeType = ReactNodeTypes.getType(nextRenderedElement);
|
||||
var nodeType = ReactNodeTypes.getType(nextRenderedElement);
|
||||
this._renderedNodeType = nodeType;
|
||||
var child = this._instantiateReactComponent(
|
||||
nextRenderedElement
|
||||
nextRenderedElement,
|
||||
nodeType !== ReactNodeTypes.EMPTY /* shouldHaveDebugID */
|
||||
);
|
||||
this._renderedComponent = child;
|
||||
if (__DEV__) {
|
||||
|
||||
@@ -162,10 +162,15 @@ if (__DEV__) {
|
||||
}
|
||||
};
|
||||
setChildrenForInstrumentation = function(children) {
|
||||
ReactInstrumentation.debugTool.onSetChildren(
|
||||
getDebugID(this),
|
||||
children ? Object.keys(children).map(key => children[key]._debugID) : []
|
||||
);
|
||||
var debugID = getDebugID(this);
|
||||
// TODO: React Native empty components are also multichild.
|
||||
// This means they still get into this method but don't have _debugID.
|
||||
if (debugID !== 0) {
|
||||
ReactInstrumentation.debugTool.onSetChildren(
|
||||
debugID,
|
||||
children ? Object.keys(children).map(key => children[key]._debugID) : []
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -78,14 +78,14 @@ var nextDebugID = 1;
|
||||
* Given a ReactNode, create an instance that will actually be mounted.
|
||||
*
|
||||
* @param {ReactNode} node
|
||||
* @param {boolean} shouldHaveDebugID
|
||||
* @return {object} A new instance of the element's constructor.
|
||||
* @protected
|
||||
*/
|
||||
function instantiateReactComponent(node) {
|
||||
function instantiateReactComponent(node, shouldHaveDebugID) {
|
||||
var instance;
|
||||
|
||||
var isEmpty = node === null || node === false;
|
||||
if (isEmpty) {
|
||||
if (node === null || node === false) {
|
||||
instance = ReactEmptyComponent.create(instantiateReactComponent);
|
||||
} else if (typeof node === 'object') {
|
||||
var element = node;
|
||||
@@ -141,16 +141,17 @@ function instantiateReactComponent(node) {
|
||||
instance._mountImage = null;
|
||||
|
||||
if (__DEV__) {
|
||||
var debugID = isEmpty ? 0 : nextDebugID++;
|
||||
instance._debugID = debugID;
|
||||
|
||||
if (debugID !== 0) {
|
||||
if (shouldHaveDebugID) {
|
||||
var debugID = 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);
|
||||
}
|
||||
} else {
|
||||
instance._debugID = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -120,16 +120,10 @@ var ReactHostMount = {
|
||||
// }
|
||||
// }
|
||||
|
||||
var instance = instantiateReactComponent(nextWrappedElement);
|
||||
var instance = instantiateReactComponent(nextWrappedElement, false);
|
||||
|
||||
if (__DEV__) {
|
||||
// Mute future events from the top level wrapper.
|
||||
// It is an implementation detail that devtools should not know about.
|
||||
instance._debugID = 0;
|
||||
|
||||
if (__DEV__) {
|
||||
ReactInstrumentation.debugTool.onBeginFlush();
|
||||
}
|
||||
ReactInstrumentation.debugTool.onBeginFlush();
|
||||
}
|
||||
|
||||
// The initial render is synchronous but any updates that happen during
|
||||
|
||||
Reference in new issue
Block a user