Skip to content

Commit d0883c8

Browse files
committed
Use depth (not owner) to check for root components
Fixes #557.
1 parent 09bdcef commit d0883c8

File tree

2 files changed

+56
-8
lines changed

2 files changed

+56
-8
lines changed

src/core/ReactComponent.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -260,17 +260,17 @@ var ReactComponent = {
260260
*/
261261
replaceProps: function(props, callback) {
262262
invariant(
263-
!this._owner,
263+
this.isMounted(),
264+
'replaceProps(...): Can only update a mounted component.'
265+
);
266+
invariant(
267+
this._mountDepth === 0,
264268
'replaceProps(...): You called `setProps` or `replaceProps` on a ' +
265-
'component with an owner. This is an anti-pattern since props will ' +
269+
'component with a parent. This is an anti-pattern since props will ' +
266270
'get reactively updated when rendered. Instead, change the owner\'s ' +
267271
'`render` method to pass the correct value as props to the component ' +
268272
'where it is created.'
269273
);
270-
invariant(
271-
this.isMounted(),
272-
'replaceProps(...): Can only update a mounted component.'
273-
);
274274
this._pendingProps = props;
275275
ReactUpdates.enqueueUpdate(this, callback);
276276
},

src/core/__tests__/ReactComponentLifeCycle-test.js

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -404,8 +404,56 @@ describe('ReactComponentLifeCycle', function() {
404404
valueToUseInitially="hello"
405405
valueToUseInOnDOMReady="goodbye"
406406
/>;
407-
expect(ReactTestUtils.renderIntoDocument.bind(ReactTestUtils, instance))
408-
.toThrow();
407+
expect(function() {
408+
ReactTestUtils.renderIntoDocument(instance)
409+
}).toThrow(
410+
'Invariant Violation: replaceProps(...): You called `setProps` or ' +
411+
'`replaceProps` on a component with a parent. This is an anti-pattern ' +
412+
'since props will get reactively updated when rendered. Instead, ' +
413+
'change the owner\'s `render` method to pass the correct value as ' +
414+
'props to the component where it is created.'
415+
);
416+
});
417+
418+
it('should not throw when updating an auxiliary component', function() {
419+
var Tooltip = React.createClass({
420+
render: function() {
421+
return <div>{this.props.children}</div>;
422+
},
423+
componentDidMount: function() {
424+
this.container = document.createElement('div');
425+
this.updateTooltip();
426+
},
427+
componentDidUpdate: function() {
428+
this.updateTooltip();
429+
},
430+
updateTooltip: function() {
431+
// Even though this.props.tooltip has an owner, updating it shouldn't
432+
// throw here because it's mounted as a root component
433+
React.renderComponent(this.props.tooltip, this.container);
434+
}
435+
});
436+
var Component = React.createClass({
437+
render: function() {
438+
return (
439+
<Tooltip
440+
ref="tooltip"
441+
tooltip={<div>{this.props.tooltipText}</div>}>
442+
{this.props.text}
443+
</Tooltip>
444+
);
445+
}
446+
});
447+
448+
var container = document.createElement('div');
449+
var instance = React.renderComponent(
450+
<Component text="uno" tooltipText="one" />,
451+
container
452+
);
453+
454+
// Since `instance` is a root component, we can set its props. This also
455+
// makes Tooltip rerender the tooltip component, which shouldn't throw.
456+
instance.setProps({text: "dos", tooltipText: "two"});
409457
});
410458

411459
it('should throw when calling setProps() on an unmounted component',

0 commit comments

Comments
 (0)