From fd43666ca827e2b72971836557b9a2ff661f0502 Mon Sep 17 00:00:00 2001 From: Jim Date: Mon, 25 Apr 2016 12:36:20 -0700 Subject: [PATCH] ComponentWillUnmount should only ever be invoked once --- .../reconciler/ReactCompositeComponent.js | 6 ++- .../__tests__/ReactCompositeComponent-test.js | 41 +++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/src/renderers/shared/reconciler/ReactCompositeComponent.js b/src/renderers/shared/reconciler/ReactCompositeComponent.js index 907de98089ec0..d6ea8b123312f 100644 --- a/src/renderers/shared/reconciler/ReactCompositeComponent.js +++ b/src/renderers/shared/reconciler/ReactCompositeComponent.js @@ -132,6 +132,9 @@ var ReactCompositeComponentMixin = { // See ReactUpdates and ReactUpdateQueue. this._pendingCallbacks = null; + + // ComponentWillUnmount shall only be called once + this._calledComponentWillUnmount = false; }, /** @@ -405,7 +408,8 @@ var ReactCompositeComponentMixin = { } var inst = this._instance; - if (inst.componentWillUnmount) { + if (inst.componentWillUnmount && !inst._calledComponentWillUnmount) { + inst._calledComponentWillUnmount = true; if (safely) { var name = this.getName() + '.componentWillUnmount()'; ReactErrorUtils.invokeGuardedCallback(name, inst.componentWillUnmount.bind(inst)); diff --git a/src/renderers/shared/reconciler/__tests__/ReactCompositeComponent-test.js b/src/renderers/shared/reconciler/__tests__/ReactCompositeComponent-test.js index c33fb33b25382..5568ae33931ba 100644 --- a/src/renderers/shared/reconciler/__tests__/ReactCompositeComponent-test.js +++ b/src/renderers/shared/reconciler/__tests__/ReactCompositeComponent-test.js @@ -1303,4 +1303,45 @@ describe('ReactCompositeComponent', function() { }); + it('should only call componentWillUnmount once', function() { + var app; + var count = 0; + + class App extends React.Component { + render() { + if (this.props.stage === 1) { + return ; + } else { + return null; + } + } + } + + class UnunmountableComponent extends React.Component { + componentWillUnmount() { + app.setState({}); + count++; + throw Error('always fails'); + } + + render() { + return
Hello {this.props.name}
; + } + } + + var container = document.createElement('div'); + + var setRef = (ref) => { + if (ref) { + app = ref; + } + }; + + expect(function() { + ReactDOM.render(, container); + ReactDOM.render(, container); + }).toThrow(); + expect(count).toBe(1); + }); + });