From aea8e16b4a32d179e70089edde6f1892968cf2b8 Mon Sep 17 00:00:00 2001 From: CommitSyncScript Date: Thu, 13 Jun 2013 16:32:52 -0700 Subject: [PATCH] Add `ReactComponent#isMounted` There is currently no way for components to know whether or not they are mounted. This means there's no way for callbacks to figure out if they can make certain assumptions (e.g. can `getDOMNode()` or `setState()` be safely invoked). This adds an `isMounted` protected method that lets components properly handle callback behavior when unmounted. --- src/core/ReactComponent.js | 19 +++++++++++++++---- src/core/ReactCompositeComponent.js | 4 ++-- .../__tests__/ReactNativeComponent-test.js | 10 +++++++--- 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/src/core/ReactComponent.js b/src/core/ReactComponent.js index fe704dfbbf..b9aa2330c4 100644 --- a/src/core/ReactComponent.js +++ b/src/core/ReactComponent.js @@ -188,6 +188,17 @@ var ReactComponent = { */ Mixin: { + /** + * Checks whether or not this component is mounted. + * + * @return {boolean} True if mounted, false otherwise. + * @final + * @protected + */ + isMounted: function() { + return this._lifeCycleState === ComponentLifeCycle.MOUNTED; + }, + /** * Returns the DOM node rendered by this component. * @@ -201,7 +212,7 @@ var ReactComponent = { 'getDOMNode(): The DOM is not supported in the current environment.' ); invariant( - this._lifeCycleState === ComponentLifeCycle.MOUNTED, + this.isMounted(), 'getDOMNode(): A component must be mounted to have a DOM node.' ); var rootNode = this._rootNode; @@ -329,7 +340,7 @@ var ReactComponent = { */ mountComponent: function(rootID, transaction) { invariant( - this._lifeCycleState === ComponentLifeCycle.UNMOUNTED, + !this.isMounted(), 'mountComponent(%s, ...): Can only mount an unmounted component.', rootID ); @@ -354,7 +365,7 @@ var ReactComponent = { */ unmountComponent: function() { invariant( - this._lifeCycleState === ComponentLifeCycle.MOUNTED, + this.isMounted(), 'unmountComponent(): Can only unmount a mounted component.' ); var props = this.props; @@ -378,7 +389,7 @@ var ReactComponent = { */ receiveProps: function(nextProps, transaction) { invariant( - this._lifeCycleState === ComponentLifeCycle.MOUNTED, + this.isMounted(), 'receiveProps(...): Can only update a mounted component.' ); var props = this.props; diff --git a/src/core/ReactCompositeComponent.js b/src/core/ReactCompositeComponent.js index cfbe109833..3fed7ad324 100644 --- a/src/core/ReactCompositeComponent.js +++ b/src/core/ReactCompositeComponent.js @@ -548,7 +548,7 @@ var ReactCompositeComponentMixin = { replaceState: function(completeState) { var compositeLifeCycleState = this._compositeLifeCycleState; invariant( - this._lifeCycleState === ReactComponent.LifeCycle.MOUNTED || + this.isMounted() || compositeLifeCycleState === CompositeLifeCycle.MOUNTING, 'replaceState(...): Can only update a mounted (or mounting) component.' ); @@ -712,7 +712,7 @@ var ReactCompositeComponentMixin = { forceUpdate: function() { var compositeLifeCycleState = this._compositeLifeCycleState; invariant( - this._lifeCycleState === ReactComponent.LifeCycle.MOUNTED, + this.isMounted(), 'forceUpdate(...): Can only force an update on mounted components.' ); invariant( diff --git a/src/core/__tests__/ReactNativeComponent-test.js b/src/core/__tests__/ReactNativeComponent-test.js index ee7e986593..54e4bc5504 100644 --- a/src/core/__tests__/ReactNativeComponent-test.js +++ b/src/core/__tests__/ReactNativeComponent-test.js @@ -241,17 +241,21 @@ describe('ReactNativeComponent', function() { var mixInto = require('mixInto'); var ReactComponent = require('ReactComponent'); + var ReactMultiChild = require('ReactMultiChild'); var ReactNativeComponent = require('ReactNativeComponent'); var ReactReconcileTransaction = require('ReactReconcileTransaction'); - var NodeStub = function(initialProps) { + var StubNativeComponent = function(initialProps) { ReactComponent.Mixin.construct.call(this, initialProps); }; - mixInto(NodeStub, ReactNativeComponent.Mixin); + mixInto(StubNativeComponent, ReactComponent.Mixin); + mixInto(StubNativeComponent, ReactNativeComponent.Mixin); + mixInto(StubNativeComponent, ReactMultiChild.Mixin); mountComponent = function(props) { var transaction = new ReactReconcileTransaction(); - return (new NodeStub(props)).mountComponent('test', transaction); + var stubComponent = new StubNativeComponent(props); + return stubComponent.mountComponent('test', transaction); }; });