mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Replace mountDepth with isTopLevel
Summary: After #2570, `mountDepth` is only used to enforce that `setProps` and `replaceProps` is only invoked on the top-level component. This replaces `mountDepth` with a simpler `isTopLevel` boolean set by `ReactMount` which reduces the surface area of the internal API and removes the need to thread `mountDepth` throughout React core. Reviewers: @sebmarkbage @zpao Test Plan: Ran unit tests successfully: ``` npm run jest ```
This commit is contained in:
@@ -40,7 +40,7 @@ function renderToString(element, context) {
|
||||
|
||||
return transaction.perform(function() {
|
||||
var componentInstance = instantiateReactComponent(element, null);
|
||||
var markup = componentInstance.mountComponent(id, transaction, 0, context);
|
||||
var markup = componentInstance.mountComponent(id, transaction, context);
|
||||
return ReactMarkupChecksum.addChecksumToMarkup(markup);
|
||||
}, null);
|
||||
} finally {
|
||||
@@ -68,7 +68,7 @@ function renderToStaticMarkup(element, context) {
|
||||
|
||||
return transaction.perform(function() {
|
||||
var componentInstance = instantiateReactComponent(element, null);
|
||||
return componentInstance.mountComponent(id, transaction, 0, context);
|
||||
return componentInstance.mountComponent(id, transaction, context);
|
||||
}, null);
|
||||
} finally {
|
||||
ReactServerRenderingTransaction.release(transaction);
|
||||
|
||||
@@ -168,15 +168,13 @@ ReactDOMComponent.Mixin = {
|
||||
* @internal
|
||||
* @param {string} rootID The root DOM ID for this node.
|
||||
* @param {ReactReconcileTransaction|ReactServerRenderingTransaction} transaction
|
||||
* @param {number} mountDepth number of components in the owner hierarchy
|
||||
* @return {string} The computed markup.
|
||||
*/
|
||||
mountComponent: function(rootID, transaction, mountDepth, context) {
|
||||
mountComponent: function(rootID, transaction, context) {
|
||||
ReactComponent.Mixin.mountComponent.call(
|
||||
this,
|
||||
rootID,
|
||||
transaction,
|
||||
mountDepth,
|
||||
context
|
||||
);
|
||||
this._rootNodeID = rootID;
|
||||
|
||||
@@ -62,11 +62,10 @@ assign(ReactDOMTextComponent.prototype, {
|
||||
*
|
||||
* @param {string} rootID DOM ID of the root node.
|
||||
* @param {ReactReconcileTransaction|ReactServerRenderingTransaction} transaction
|
||||
* @param {number} mountDepth number of components in the owner hierarchy
|
||||
* @return {string} Markup for this text node.
|
||||
* @internal
|
||||
*/
|
||||
mountComponent: function(rootID, transaction, mountDepth, context) {
|
||||
mountComponent: function(rootID, transaction, context) {
|
||||
this._rootNodeID = rootID;
|
||||
var escapedText = escapeTextForBrowser(this._stringText);
|
||||
|
||||
|
||||
@@ -225,7 +225,8 @@ function mountComponentIntoNode(
|
||||
container,
|
||||
transaction,
|
||||
shouldReuseMarkup) {
|
||||
var markup = this.mountComponent(rootID, transaction, 0, emptyObject);
|
||||
var markup = this.mountComponent(rootID, transaction, emptyObject);
|
||||
this._isTopLevel = true;
|
||||
ReactMount._mountImageIntoNode(markup, container, shouldReuseMarkup);
|
||||
}
|
||||
|
||||
|
||||
@@ -328,7 +328,7 @@ describe('ReactDOMComponent', function() {
|
||||
_owner: null,
|
||||
_context: null
|
||||
});
|
||||
return stubComponent.mountComponent('test', transaction, 0, {});
|
||||
return stubComponent.mountComponent('test', transaction, {});
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ describe('Danger', function() {
|
||||
it('should render markup', function() {
|
||||
var markup = instantiateReactComponent(
|
||||
<div />
|
||||
).mountComponent('.rX', transaction, 0, {});
|
||||
).mountComponent('.rX', transaction, {});
|
||||
var output = Danger.dangerouslyRenderMarkup([markup])[0];
|
||||
|
||||
expect(output.nodeName).toBe('DIV');
|
||||
@@ -44,7 +44,7 @@ describe('Danger', function() {
|
||||
).mountComponent(
|
||||
'.rX',
|
||||
transaction,
|
||||
0, {}
|
||||
{}
|
||||
);
|
||||
var output = Danger.dangerouslyRenderMarkup([markup])[0];
|
||||
|
||||
@@ -55,7 +55,7 @@ describe('Danger', function() {
|
||||
it('should render wrapped markup', function() {
|
||||
var markup = instantiateReactComponent(
|
||||
<th />
|
||||
).mountComponent('.rX', transaction, 0, {});
|
||||
).mountComponent('.rX', transaction, {});
|
||||
var output = Danger.dangerouslyRenderMarkup([markup])[0];
|
||||
|
||||
expect(output.nodeName).toBe('TH');
|
||||
|
||||
@@ -90,7 +90,6 @@ var ReactComponent = {
|
||||
// to track updates.
|
||||
this._currentElement = element;
|
||||
this._mountIndex = 0;
|
||||
this._mountDepth = 0;
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -103,17 +102,15 @@ var ReactComponent = {
|
||||
*
|
||||
* @param {string} rootID DOM ID of the root node.
|
||||
* @param {ReactReconcileTransaction|ReactServerRenderingTransaction} transaction
|
||||
* @param {number} mountDepth number of components in the owner hierarchy.
|
||||
* @return {?string} Rendered markup to be inserted into the DOM.
|
||||
* @internal
|
||||
*/
|
||||
mountComponent: function(rootID, transaction, mountDepth, context) {
|
||||
mountComponent: function(rootID, transaction, context) {
|
||||
var ref = this._currentElement.ref;
|
||||
if (ref != null) {
|
||||
var owner = this._currentElement._owner;
|
||||
attachRef(ref, this, owner);
|
||||
}
|
||||
this._mountDepth = mountDepth;
|
||||
// Effectively: return '';
|
||||
},
|
||||
|
||||
|
||||
@@ -141,6 +141,7 @@ var ReactCompositeComponentMixin = assign({},
|
||||
|
||||
this._context = null;
|
||||
this._mountOrder = 0;
|
||||
this._isTopLevel = false;
|
||||
|
||||
// See ReactUpdates.
|
||||
this._pendingCallbacks = null;
|
||||
@@ -161,17 +162,15 @@ var ReactCompositeComponentMixin = assign({},
|
||||
*
|
||||
* @param {string} rootID DOM ID of the root node.
|
||||
* @param {ReactReconcileTransaction|ReactServerRenderingTransaction} transaction
|
||||
* @param {number} mountDepth number of components in the owner hierarchy
|
||||
* @return {?string} Rendered markup to be inserted into the DOM.
|
||||
* @final
|
||||
* @internal
|
||||
*/
|
||||
mountComponent: function(rootID, transaction, mountDepth, context) {
|
||||
mountComponent: function(rootID, transaction, context) {
|
||||
ReactComponent.Mixin.mountComponent.call(
|
||||
this,
|
||||
rootID,
|
||||
transaction,
|
||||
mountDepth,
|
||||
context
|
||||
);
|
||||
|
||||
@@ -233,7 +232,6 @@ var ReactCompositeComponentMixin = assign({},
|
||||
var markup = this._renderedComponent.mountComponent(
|
||||
rootID,
|
||||
transaction,
|
||||
mountDepth + 1,
|
||||
this._processChildContext(context)
|
||||
);
|
||||
if (inst.componentDidMount) {
|
||||
@@ -313,7 +311,7 @@ var ReactCompositeComponentMixin = assign({},
|
||||
*/
|
||||
replaceProps: function(props, callback) {
|
||||
invariant(
|
||||
this._mountDepth === 0,
|
||||
this._isTopLevel,
|
||||
'replaceProps(...): You called `setProps` or `replaceProps` on a ' +
|
||||
'component with a parent. This is an anti-pattern since props will ' +
|
||||
'get reactively updated when rendered. Instead, change the owner\'s ' +
|
||||
@@ -781,7 +779,6 @@ var ReactCompositeComponentMixin = assign({},
|
||||
var nextMarkup = this._renderedComponent.mountComponent(
|
||||
thisID,
|
||||
transaction,
|
||||
this._mountDepth + 1,
|
||||
context
|
||||
);
|
||||
ReactComponentEnvironment.replaceNodeWithMarkupByID(
|
||||
@@ -890,17 +887,15 @@ var ShallowMixin = assign({},
|
||||
*
|
||||
* @param {string} rootID DOM ID of the root node.
|
||||
* @param {ReactReconcileTransaction|ReactServerRenderingTransaction} transaction
|
||||
* @param {number} mountDepth number of components in the owner hierarchy
|
||||
* @return {ReactElement} Shallow rendering of the component.
|
||||
* @final
|
||||
* @internal
|
||||
*/
|
||||
mountComponent: function(rootID, transaction, mountDepth, context) {
|
||||
mountComponent: function(rootID, transaction, context) {
|
||||
ReactComponent.Mixin.mountComponent.call(
|
||||
this,
|
||||
rootID,
|
||||
transaction,
|
||||
mountDepth,
|
||||
context
|
||||
);
|
||||
|
||||
|
||||
@@ -195,7 +195,6 @@ var ReactMultiChild = {
|
||||
var mountImage = childInstance.mountComponent(
|
||||
rootID,
|
||||
transaction,
|
||||
this._mountDepth + 1,
|
||||
context
|
||||
);
|
||||
childInstance._mountIndex = index;
|
||||
@@ -401,7 +400,6 @@ var ReactMultiChild = {
|
||||
var mountImage = child.mountComponent(
|
||||
rootID,
|
||||
transaction,
|
||||
this._mountDepth + 1,
|
||||
context
|
||||
);
|
||||
child._mountIndex = index;
|
||||
|
||||
@@ -16,7 +16,6 @@ var ReactInstanceMap;
|
||||
var ReactTestUtils;
|
||||
|
||||
var reactComponentExpect;
|
||||
var getMountDepth;
|
||||
|
||||
describe('ReactComponent', function() {
|
||||
beforeEach(function() {
|
||||
@@ -24,10 +23,6 @@ describe('ReactComponent', function() {
|
||||
ReactInstanceMap = require('ReactInstanceMap');
|
||||
ReactTestUtils = require('ReactTestUtils');
|
||||
reactComponentExpect = require('reactComponentExpect');
|
||||
|
||||
getMountDepth = function(instance) {
|
||||
return ReactInstanceMap.get(instance)._mountDepth;
|
||||
};
|
||||
});
|
||||
|
||||
it('should throw on invalid render targets', function() {
|
||||
@@ -231,80 +226,4 @@ describe('ReactComponent', function() {
|
||||
var instance = ReactTestUtils.renderIntoDocument(element);
|
||||
expect(instance.isMounted()).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should know its simple mount depth', function() {
|
||||
var Owner = React.createClass({
|
||||
render: function() {
|
||||
return <Child ref="child" />;
|
||||
}
|
||||
});
|
||||
|
||||
var Child = React.createClass({
|
||||
render: function() {
|
||||
return <div />;
|
||||
}
|
||||
});
|
||||
|
||||
var instance = <Owner />;
|
||||
instance = ReactTestUtils.renderIntoDocument(instance);
|
||||
expect(getMountDepth(instance)).toBe(0);
|
||||
expect(getMountDepth(instance.refs.child)).toBe(1);
|
||||
});
|
||||
|
||||
it('should know its (complicated) mount depth', function() {
|
||||
var Box = React.createClass({
|
||||
render: function() {
|
||||
return <div ref="boxDiv">{this.props.children}</div>;
|
||||
}
|
||||
});
|
||||
|
||||
var Child = React.createClass({
|
||||
render: function() {
|
||||
return <span ref="span">child</span>;
|
||||
}
|
||||
});
|
||||
|
||||
var Switcher = React.createClass({
|
||||
getInitialState: function() {
|
||||
return {tabKey: 'hello'};
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var child = this.props.children;
|
||||
|
||||
return (
|
||||
<Box ref="box">
|
||||
<div
|
||||
ref="switcherDiv"
|
||||
style={{
|
||||
display: this.state.tabKey === child.key ? '' : 'none'
|
||||
}}>
|
||||
{child}
|
||||
</div>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var App = React.createClass({
|
||||
render: function() {
|
||||
return (
|
||||
<Switcher ref="switcher">
|
||||
<Child key="hello" ref="child" />
|
||||
</Switcher>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var root = <App />;
|
||||
root = ReactTestUtils.renderIntoDocument(root);
|
||||
|
||||
expect(getMountDepth(root)).toBe(0);
|
||||
expect(getMountDepth(root.refs.switcher)).toBe(1);
|
||||
expect(getMountDepth(root.refs.switcher.refs.box)).toBe(2);
|
||||
expect(getMountDepth(root.refs.switcher.refs.switcherDiv)).toBe(5);
|
||||
expect(getMountDepth(root.refs.child)).toBe(7);
|
||||
expect(getMountDepth(root.refs.switcher.refs.box.refs.boxDiv)).toBe(3);
|
||||
expect(getMountDepth(root.refs.child.refs.span)).toBe(8);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -529,6 +529,34 @@ describe('ReactCompositeComponent', function() {
|
||||
);
|
||||
});
|
||||
|
||||
it('should only allow `setProps` on top-level components', function() {
|
||||
var container = document.createElement('div');
|
||||
document.documentElement.appendChild(container);
|
||||
|
||||
var innerInstance;
|
||||
|
||||
var Component = React.createClass({
|
||||
render: function() {
|
||||
return <div><div ref="inner" /></div>;
|
||||
},
|
||||
componentDidMount: function() {
|
||||
innerInstance = this.refs.inner;
|
||||
}
|
||||
});
|
||||
React.render(<Component />, container);
|
||||
|
||||
expect(innerInstance).not.toBe(undefined);
|
||||
expect(function() {
|
||||
innerInstance.setProps({value: 1});
|
||||
}).toThrow(
|
||||
'Invariant Violation: replaceProps(...): You called `setProps` or ' +
|
||||
'`replaceProps` on a component with a parent. This is an anti-pattern ' +
|
||||
'since props will get reactively updated when rendered. Instead, ' +
|
||||
'change the owner\'s `render` method to pass the correct value as ' +
|
||||
'props to the component where it is created.'
|
||||
);
|
||||
});
|
||||
|
||||
it('should cleanup even if render() fatals', function() {
|
||||
var BadComponent = React.createClass({
|
||||
render: function() {
|
||||
|
||||
@@ -340,7 +340,7 @@ ReactShallowRenderer.prototype._render = function(element, transaction, context)
|
||||
var instance = new ShallowComponentWrapper(new element.type(element.props));
|
||||
instance.construct(element);
|
||||
|
||||
instance.mountComponent(rootID, transaction, 0, context);
|
||||
instance.mountComponent(rootID, transaction, context);
|
||||
|
||||
this._instance = instance;
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user