diff --git a/src/renderers/shared/reconciler/ReactOwner.js b/src/renderers/shared/reconciler/ReactOwner.js index 164f501368188..df2bfb8d7b8e6 100644 --- a/src/renderers/shared/reconciler/ReactOwner.js +++ b/src/renderers/shared/reconciler/ReactOwner.js @@ -95,9 +95,10 @@ var ReactOwner = { '`render` method, or you have multiple copies of React loaded ' + '(details: https://fb.me/react-refs-must-have-owner).' ); - // Check that `component` is still the current ref because we do not want to - // detach the ref if another component stole it. - if (owner.getPublicInstance().refs[ref] === component.getPublicInstance()) { + var ownerPublicInstance = owner.getPublicInstance(); + // Check that `component`'s owner is still alive and that `component` is still the current ref + // because we do not want to detach the ref if another component stole it. + if (ownerPublicInstance && ownerPublicInstance.refs[ref] === component.getPublicInstance()) { owner.detachRef(ref); } }, diff --git a/src/renderers/shared/reconciler/__tests__/refs-destruction-test.js b/src/renderers/shared/reconciler/__tests__/refs-destruction-test.js index 2fb43167bd07c..0d001c22540bd 100644 --- a/src/renderers/shared/reconciler/__tests__/refs-destruction-test.js +++ b/src/renderers/shared/reconciler/__tests__/refs-destruction-test.js @@ -59,4 +59,46 @@ describe('refs-destruction', function() { ReactDOM.render(, container); expect(Object.keys(testInstance.refs || {}).length).toEqual(0); }); + + it('should not error when destroying child with ref asynchronously', function() { + var Modal = React.createClass({ + componentDidMount: function() { + this.div = document.createElement('div'); + document.body.appendChild(this.div); + this.componentDidUpdate(); + }, + componentDidUpdate: function() { + ReactDOM.render(
{this.props.children}
, this.div); + }, + componentWillUnmount: function() { + var self = this; + // some async animation + setTimeout(function() { + expect(function() { + ReactDOM.unmountComponentAtNode(self.div); + }).not.toThrow(); + document.body.removeChild(self.div); + }, 0); + }, + render() { + return null; + }, + }); + var AppModal = React.createClass({ + render: function() { + return ( + + ); + }, + }); + var App = React.createClass({ + render: function() { + return this.props.hidden ? null : ; + }, + }); + var container = document.createElement('div'); + ReactDOM.render(, container); + ReactDOM.render(