@samitouri / QOS-React / commits / df095c005f

fix instance null when involve async destruction

yiminghe committed Feb 25, 2016 at 15:42 UTC df095c005ffae2c428860147e81b17b855c67a20
2 files changed +46 -3
src/renderers/shared/reconciler/ReactOwner.js
+4 -3
@@ -95,9 +95,10 @@ var ReactOwner = {
95 '`render` method, or you have multiple copies of React loaded ' +
96 '(details: https://fb.me/react-refs-must-have-owner).'
97 );
98 - // Check that `component` is still the current ref because we do not want to
99 - // detach the ref if another component stole it.
100 - if (owner.getPublicInstance().refs[ref] === component.getPublicInstance()) {
98 + var ownerPublicInstance = owner.getPublicInstance();
99 + // Check that `component`'s owner is still alive and that `component` is still the current ref
100 + // because we do not want to detach the ref if another component stole it.
101 + if (ownerPublicInstance && ownerPublicInstance.refs[ref] === component.getPublicInstance()) {
102 owner.detachRef(ref);
103 }
104 },
src/renderers/shared/reconciler/__tests__/refs-destruction-test.js
+42
@@ -59,4 +59,46 @@ describe('refs-destruction', function() {
59 ReactDOM.render(<TestComponent destroy={true} />, container);
60 expect(Object.keys(testInstance.refs || {}).length).toEqual(0);
61 });
62 +
63 + it('should not error when destroying child with ref asynchronously', function() {
64 + var Modal = React.createClass({
65 + componentDidMount: function() {
66 + this.div = document.createElement('div');
67 + document.body.appendChild(this.div);
68 + this.componentDidUpdate();
69 + },
70 + componentDidUpdate: function() {
71 + ReactDOM.render(<div>{this.props.children}</div>, this.div);
72 + },
73 + componentWillUnmount: function() {
74 + var self = this;
75 + // some async animation
76 + setTimeout(function() {
77 + expect(function() {
78 + ReactDOM.unmountComponentAtNode(self.div);
79 + }).not.toThrow();
80 + document.body.removeChild(self.div);
81 + }, 0);
82 + },
83 + render() {
84 + return null;
85 + },
86 + });
87 + var AppModal = React.createClass({
88 + render: function() {
89 + return (<Modal>
90 + <a ref="ref"/>
91 + </Modal>);
92 + },
93 + });
94 + var App = React.createClass({
95 + render: function() {
96 + return this.props.hidden ? null : <AppModal onClose={this.close}/>;
97 + },
98 + });
99 + var container = document.createElement('div');
100 + ReactDOM.render(<App />, container);
101 + ReactDOM.render(<App hidden={true}/>, container);
102 + jest.runAllTimers();
103 + });
104 });