Skip to content

Commit

Permalink
Batch updates within top-level unmount
Browse files Browse the repository at this point in the history
Analogous change to facebook#2935.
  • Loading branch information
sophiebits committed Apr 30, 2015
1 parent a092b47 commit 36f3a2a
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 23 deletions.
50 changes: 27 additions & 23 deletions src/browser/ui/ReactMount.js
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,28 @@ function batchedMountComponentIntoNode(
ReactUpdates.ReactReconcileTransaction.release(transaction);
}

/**
* Unmounts a component and removes it from the DOM.
*
* @param {ReactComponent} instance React component instance.
* @param {DOMElement} container DOM element to unmount from.
* @final
* @internal
* @see {ReactMount.unmountComponentAtNode}
*/
function unmountComponentFromNode(instance, container) {
ReactReconciler.unmountComponent(instance);

if (container.nodeType === DOC_NODE_TYPE) {
container = container.documentElement;
}

// http://jsperf.com/emptying-a-node
while (container.lastChild) {
container.removeChild(container.lastChild);
}
}

/**
* Mounting is the process of initializing a React component by creating its
* representative DOM elements and inserting them into a supplied `container`.
Expand Down Expand Up @@ -668,7 +690,11 @@ var ReactMount = {
if (!component) {
return false;
}
ReactMount.unmountComponentFromNode(component, container);
ReactUpdates.batchedUpdates(
unmountComponentFromNode,
component,
container
);
delete instancesByReactRootID[reactRootID];
delete containersByReactRootID[reactRootID];
if (__DEV__) {
Expand All @@ -677,28 +703,6 @@ var ReactMount = {
return true;
},

/**
* Unmounts a component and removes it from the DOM.
*
* @param {ReactComponent} instance React component instance.
* @param {DOMElement} container DOM element to unmount from.
* @final
* @internal
* @see {ReactMount.unmountComponentAtNode}
*/
unmountComponentFromNode: function(instance, container) {
ReactReconciler.unmountComponent(instance);

if (container.nodeType === DOC_NODE_TYPE) {
container = container.documentElement;
}

// http://jsperf.com/emptying-a-node
while (container.lastChild) {
container.removeChild(container.lastChild);
}
},

/**
* Finds the container DOM element that contains React component to which the
* supplied DOM `id` belongs.
Expand Down
28 changes: 28 additions & 0 deletions src/core/__tests__/ReactCompositeComponentState-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -218,4 +218,32 @@ describe('ReactCompositeComponent-state', function() {
['componentWillUnmount', 'blue']
]);
});

it('should batch unmounts', function() {
var outer;
var Inner = React.createClass({
render: function() {
return <div />;
},
componentWillUnmount: function() {
// This should get silently ignored (maybe with a warning), but it
// shouldn't break React.
outer.setState({showInner: false});
}
});
var Outer = React.createClass({
getInitialState: function() {
return {showInner: true};
},
render: function() {
return <div>{this.state.showInner && <Inner />}</div>;
}
});

var container = document.createElement('div');
outer = React.render(<Outer />, container);
expect(() => {
React.unmountComponentAtNode(container);
}).not.toThrow();
});
});

0 comments on commit 36f3a2a

Please sign in to comment.