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
});