Merge pull request #786 from spicyj/gh-781

Fix potential memory leak when unmounting
This commit is contained in:
Paul O’Shannessy
2014-01-09 13:08:07 -08:00
3 changed files with 41 additions and 2 deletions
+2 -1
View File
@@ -736,10 +736,11 @@ var ReactCompositeComponentMixin = {
this._defaultProps = null;
ReactComponent.Mixin.unmountComponent.call(this);
this._renderedComponent.unmountComponent();
this._renderedComponent = null;
ReactComponent.Mixin.unmountComponent.call(this);
if (this.refs) {
this.refs = null;
}
+1 -1
View File
@@ -380,9 +380,9 @@ ReactDOMComponent.Mixin = {
* @internal
*/
unmountComponent: function() {
this.unmountChildren();
ReactEventEmitter.deleteAllListeners(this._rootNodeID);
ReactComponent.Mixin.unmountComponent.call(this);
this.unmountChildren();
}
};
@@ -22,6 +22,7 @@
var MorphingComponent;
var ChildUpdates;
var React;
var ReactComponent;
var ReactCurrentOwner;
var ReactPropTypes;
var ReactTestUtils;
@@ -40,6 +41,7 @@ describe('ReactCompositeComponent', function() {
reactComponentExpect = require('reactComponentExpect');
React = require('React');
ReactComponent = require('ReactComponent');
ReactCurrentOwner = require('ReactCurrentOwner');
ReactDoNotBindDeprecated = require('ReactDoNotBindDeprecated');
ReactPropTypes = require('ReactPropTypes');
@@ -583,6 +585,42 @@ describe('ReactCompositeComponent', function() {
});
});
it('should call componentWillUnmount before unmounting', function() {
var container = document.createElement('div');
var innerUnmounted = false;
spyOn(ReactMount, 'purgeID').andCallThrough();
var Component = React.createClass({
render: function() {
return <div>
<Inner />
</div>;
}
});
var Inner = React.createClass({
componentWillUnmount: function() {
// It's important that ReactMount.purgeID be called after any component
// lifecycle methods, because a componentWillMount implementation is
// likely call this.getDOMNode(), which will repopulate the node cache
// after it's been cleared, causing a memory leak.
expect(ReactMount.purgeID.callCount).toBe(0);
innerUnmounted = true;
},
render: function() {
return <div />;
}
});
React.renderComponent(<Component />, container);
React.unmountComponentAtNode(container);
expect(innerUnmounted).toBe(true);
// <Component />, <Inner />, and both <div /> elements each call
// unmountIDFromEnvironment which calls purgeID, for a total of 4.
expect(ReactMount.purgeID.callCount).toBe(4);
});
it('should detect valid CompositeComponent classes', function() {
var Component = React.createClass({
render: function() {