@samitouri / QOS-React-2 / commits / 36f3a2aaff

Batch updates within top-level unmount

Analogous change to #2935.

Ben Alpert committed Apr 30, 2015 at 13:57 UTC 36f3a2aaff141ad36abc344c1278b22364e8f23a
2 files changed +55 -23
src/browser/ui/ReactMount.js
+27 -23
@@ -306,6 +306,28 @@ function batchedMountComponentIntoNode(
306 ReactUpdates.ReactReconcileTransaction.release(transaction);
307 }
308
309 +/**
310 + * Unmounts a component and removes it from the DOM.
311 + *
312 + * @param {ReactComponent} instance React component instance.
313 + * @param {DOMElement} container DOM element to unmount from.
314 + * @final
315 + * @internal
316 + * @see {ReactMount.unmountComponentAtNode}
317 + */
318 +function unmountComponentFromNode(instance, container) {
319 + ReactReconciler.unmountComponent(instance);
320 +
321 + if (container.nodeType === DOC_NODE_TYPE) {
322 + container = container.documentElement;
323 + }
324 +
325 + // http://jsperf.com/emptying-a-node
326 + while (container.lastChild) {
327 + container.removeChild(container.lastChild);
328 + }
329 +}
330 +
331 /**
332 * Mounting is the process of initializing a React component by creating its
333 * representative DOM elements and inserting them into a supplied `container`.
@@ -668,7 +690,11 @@ var ReactMount = {
690 if (!component) {
691 return false;
692 }
671 - ReactMount.unmountComponentFromNode(component, container);
693 + ReactUpdates.batchedUpdates(
694 + unmountComponentFromNode,
695 + component,
696 + container
697 + );
698 delete instancesByReactRootID[reactRootID];
699 delete containersByReactRootID[reactRootID];
700 if (__DEV__) {
@@ -677,28 +703,6 @@ var ReactMount = {
703 return true;
704 },
705
680 - /**
681 - * Unmounts a component and removes it from the DOM.
682 - *
683 - * @param {ReactComponent} instance React component instance.
684 - * @param {DOMElement} container DOM element to unmount from.
685 - * @final
686 - * @internal
687 - * @see {ReactMount.unmountComponentAtNode}
688 - */
689 - unmountComponentFromNode: function(instance, container) {
690 - ReactReconciler.unmountComponent(instance);
691 -
692 - if (container.nodeType === DOC_NODE_TYPE) {
693 - container = container.documentElement;
694 - }
695 -
696 - // http://jsperf.com/emptying-a-node
697 - while (container.lastChild) {
698 - container.removeChild(container.lastChild);
699 - }
700 - },
701 -
706 /**
707 * Finds the container DOM element that contains React component to which the
708 * supplied DOM `id` belongs.
src/core/__tests__/ReactCompositeComponentState-test.js
+28
@@ -218,4 +218,32 @@ describe('ReactCompositeComponent-state', function() {
218 ['componentWillUnmount', 'blue']
219 ]);
220 });
221 +
222 + it('should batch unmounts', function() {
223 + var outer;
224 + var Inner = React.createClass({
225 + render: function() {
226 + return <div />;
227 + },
228 + componentWillUnmount: function() {
229 + // This should get silently ignored (maybe with a warning), but it
230 + // shouldn't break React.
231 + outer.setState({showInner: false});
232 + }
233 + });
234 + var Outer = React.createClass({
235 + getInitialState: function() {
236 + return {showInner: true};
237 + },
238 + render: function() {
239 + return <div>{this.state.showInner && <Inner />}</div>;
240 + }
241 + });
242 +
243 + var container = document.createElement('div');
244 + outer = React.render(<Outer />, container);
245 + expect(() => {
246 + React.unmountComponentAtNode(container);
247 + }).not.toThrow();
248 + });
249 });