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