From bc362a80ce7653fa99cbbd22a746d24277a4917b Mon Sep 17 00:00:00 2001 From: romanmatiasko Date: Wed, 3 Dec 2014 01:49:38 -0800 Subject: [PATCH 1/2] add ability to remove/reset state back to null --- docs/docs/ref-02-component-api.md | 2 +- src/core/ReactCompositeComponent.js | 17 +++++++++++++++- .../ReactCompositeComponentState-test.js | 20 +++++++++++++++---- 3 files changed, 33 insertions(+), 6 deletions(-) diff --git a/docs/docs/ref-02-component-api.md b/docs/docs/ref-02-component-api.md index 2deeef703ed27..1583adf3eeb6a 100644 --- a/docs/docs/ref-02-component-api.md +++ b/docs/docs/ref-02-component-api.md @@ -36,7 +36,7 @@ Merges nextState with the current state. This is the primary method you use to t replaceState(object nextState[, function callback]) ``` -Like `setState()` but deletes any pre-existing state keys that are not in nextState. +Like `setState()` but deletes any pre-existing state keys that are not in nextState. If you pass `null` instead of object, `this.state` will be reset back to `null`. ### forceUpdate() diff --git a/src/core/ReactCompositeComponent.js b/src/core/ReactCompositeComponent.js index c3d880ce7135d..febd41c1df0fb 100644 --- a/src/core/ReactCompositeComponent.js +++ b/src/core/ReactCompositeComponent.js @@ -130,6 +130,7 @@ var ReactCompositeComponentMixin = assign({}, this._pendingContext = null; this._pendingState = null; this._pendingForceUpdate = false; + this._removeState = false; this._compositeLifeCycleState = null; this._renderedComponent = null; @@ -208,6 +209,7 @@ var ReactCompositeComponentMixin = assign({}, this._pendingState = null; this._pendingForceUpdate = false; + this._removeState = false; if (inst.componentWillMount) { inst.componentWillMount(); @@ -261,6 +263,7 @@ var ReactCompositeComponentMixin = assign({}, this._pendingForceUpdate = false; this._pendingCallbacks = null; this._pendingElement = null; + this._removeState = false; ReactComponent.Mixin.unmountComponent.call(this); @@ -378,6 +381,11 @@ var ReactCompositeComponentMixin = assign({}, replaceState: function(completeState, callback) { validateLifeCycleOnReplaceState(this); this._pendingState = completeState; + + if (completeState === null) { + this._removeState = true; + } + if (this._compositeLifeCycleState !== CompositeLifeCycle.MOUNTING) { // If we're in a componentWillMount handler, don't enqueue a rerender // because ReactUpdates assumes we're in a browser context (which is wrong @@ -563,6 +571,7 @@ var ReactCompositeComponentMixin = assign({}, if (this._pendingElement == null && this._pendingState == null && this._pendingContext == null && + !this._removeState && !this._pendingForceUpdate) { return; } @@ -678,7 +687,13 @@ var ReactCompositeComponentMixin = assign({}, this._compositeLifeCycleState = null; - var nextState = this._pendingState || inst.state; + var nextState; + if (this._removeState) { + nextState = null; + this._removeState = false; + } else { + nextState = this._pendingState || inst.state; + } this._pendingState = null; var shouldUpdate = diff --git a/src/core/__tests__/ReactCompositeComponentState-test.js b/src/core/__tests__/ReactCompositeComponentState-test.js index 10fa09a1bf929..d339b1b21bcd4 100644 --- a/src/core/__tests__/ReactCompositeComponentState-test.js +++ b/src/core/__tests__/ReactCompositeComponentState-test.js @@ -30,7 +30,7 @@ describe('ReactCompositeComponent-state', function() { TestComponent = React.createClass({ peekAtState: function(from, state) { - if (state) { + if (state !== undefined) { this.props.stateListener(from, state && state.color); } else { var internalInstance = ReactInstanceMap.get(this); @@ -48,6 +48,10 @@ describe('ReactCompositeComponent-state', function() { this.setState({color: nextColor}); }, + removeState: function() { + this.replaceState(null); + }, + getInitialState: function() { this.peekAtState('getInitialState'); return {color: 'red'}; @@ -55,7 +59,7 @@ describe('ReactCompositeComponent-state', function() { render: function() { this.peekAtState('render'); - return
{this.state.color}
; + return
{this.state && this.state.color}
; }, componentWillMount: function() { @@ -113,9 +117,9 @@ describe('ReactCompositeComponent-state', function() { instance.setProps({nextColor: 'green'}); instance.setFavoriteColor('blue'); instance.forceUpdate(); + instance.removeState(); React.unmountComponentAtNode(container); - expect(stateListener.mock.calls).toEqual([ // there is no state when getInitialState() is called [ 'getInitialState', null, null ], @@ -162,9 +166,17 @@ describe('ReactCompositeComponent-state', function() { [ 'render', 'blue', null ], [ 'componentDidUpdate-currentState', 'blue', null ], [ 'componentDidUpdate-prevState', 'blue' ], + // removeState() + [ 'shouldComponentUpdate-currentState', 'blue', null ], + [ 'shouldComponentUpdate-nextState', null ], + [ 'componentWillUpdate-currentState', 'blue', null ], + [ 'componentWillUpdate-nextState', null ], + [ 'render', null, null ], + [ 'componentDidUpdate-currentState', null, null ], + [ 'componentDidUpdate-prevState', 'blue' ], // unmountComponent() // state is available within `componentWillUnmount()` - [ 'componentWillUnmount', 'blue', null ] + [ 'componentWillUnmount', null, null ] ]); }); }); From cf1e66144bd02e2c53cedaa3af7b24ccc665060b Mon Sep 17 00:00:00 2001 From: romanmatiasko Date: Wed, 3 Dec 2014 02:23:19 -0800 Subject: [PATCH 2/2] remove redundant replaceState() method from test --- src/core/__tests__/ReactCompositeComponentState-test.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/core/__tests__/ReactCompositeComponentState-test.js b/src/core/__tests__/ReactCompositeComponentState-test.js index d339b1b21bcd4..9caf1371ee396 100644 --- a/src/core/__tests__/ReactCompositeComponentState-test.js +++ b/src/core/__tests__/ReactCompositeComponentState-test.js @@ -48,10 +48,6 @@ describe('ReactCompositeComponent-state', function() { this.setState({color: nextColor}); }, - removeState: function() { - this.replaceState(null); - }, - getInitialState: function() { this.peekAtState('getInitialState'); return {color: 'red'}; @@ -117,7 +113,7 @@ describe('ReactCompositeComponent-state', function() { instance.setProps({nextColor: 'green'}); instance.setFavoriteColor('blue'); instance.forceUpdate(); - instance.removeState(); + instance.replaceState(null); React.unmountComponentAtNode(container); expect(stateListener.mock.calls).toEqual([ @@ -166,7 +162,7 @@ describe('ReactCompositeComponent-state', function() { [ 'render', 'blue', null ], [ 'componentDidUpdate-currentState', 'blue', null ], [ 'componentDidUpdate-prevState', 'blue' ], - // removeState() + // replaceState(null) [ 'shouldComponentUpdate-currentState', 'blue', null ], [ 'shouldComponentUpdate-nextState', null ], [ 'componentWillUpdate-currentState', 'blue', null ],