Use depth (not owner) to check for root components
Fixes #557.
Ben Alpert committed
Nov 17, 2013 at 13:38 UTC
d0883c8cc74ba7253123cfe482ad993647b6693a
2 files changed
+56
-8
src/core/ReactComponent.js
+6
-6
@@ -260,17 +260,17 @@ var ReactComponent = {
260
*/
261
replaceProps: function(props, callback) {
262
invariant(
263
- !this._owner,
263
+ this.isMounted(),
264
+ 'replaceProps(...): Can only update a mounted component.'
265
+ );
266
+ invariant(
267
+ this._mountDepth === 0,
268
'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 ' +
270
'get reactively updated when rendered. Instead, change the owner\'s ' +
271
'`render` method to pass the correct value as props to the component ' +
272
'where it is created.'
273
);
270
- invariant(
271
- this.isMounted(),
272
- 'replaceProps(...): Can only update a mounted component.'
273
- );
274
this._pendingProps = props;
275
ReactUpdates.enqueueUpdate(this, callback);
276
},
src/core/__tests__/ReactComponentLifeCycle-test.js
+50
-2
@@ -404,8 +404,56 @@ describe('ReactComponentLifeCycle', function() {
404
valueToUseInitially="hello"
405
valueToUseInOnDOMReady="goodbye"
406
/>;
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"});
457
});
458
459
it('should throw when calling setProps() on an unmounted component',