From c6665e346030f73b7ebb615ce1affe8396b0dab8 Mon Sep 17 00:00:00 2001 From: ngavalas Date: Fri, 14 Jun 2013 16:23:06 -0700 Subject: [PATCH 1/4] Adds optional callback to `setState` This commit adds an optional callback as a second argument to `setState`, to be called after `setState` runs. We never guarantee synchronous execution of `setState`, and as per @phunt, we don't want to make that guarantee because we may eventually batch calls to `setState`. @jwalke agrees with him. --- src/core/ReactCompositeComponent.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/core/ReactCompositeComponent.js b/src/core/ReactCompositeComponent.js index ac2e57d93cb..03f17019f1b 100644 --- a/src/core/ReactCompositeComponent.js +++ b/src/core/ReactCompositeComponent.js @@ -525,13 +525,21 @@ var ReactCompositeComponentMixin = { * There is no guarantee that `this.state` will be immediately updated, so * accessing `this.state` after calling this method may return the old value. * + * There is no guarantee that calls to `setState` will run synchronously, + * as they may eventually be batched together. You can provide an optional + * callback that will be executed when the call to setState is actually + * completed. + * * @param {object} partialState Next partial state to be merged with state. + * @param {?function} callback Called after state is updated. * @final * @protected */ - setState: function(partialState) { + setState: function(partialState, callback) { // Merge with `_pendingState` if it exists, otherwise with existing state. this.replaceState(merge(this._pendingState || this.state, partialState)); + // If `callback` is callable, do it. + typeof callback === 'function' && callback(); }, /** From f3aac85d01d87bca593170f6ae0935c9aaf9084c Mon Sep 17 00:00:00 2001 From: ngavalas Date: Fri, 14 Jun 2013 16:37:20 -0700 Subject: [PATCH 2/4] Updated docs and check for truthiness Change api docs to reflect presence of the new argument. In addition, callback was change to require only a "truthy" value. --- docs/docs/api.md | 6 ++++-- src/core/ReactCompositeComponent.js | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/docs/api.md b/docs/docs/api.md index fd29070ecbc..cc73f2e3a37 100644 --- a/docs/docs/api.md +++ b/docs/docs/api.md @@ -108,15 +108,17 @@ Transfer properties from this component to a target component that have not alre #### setState ```javascript -setState(object nextState) +setState(object nextState_[, function callback]_) ``` -Merges nextState with the current state. This is the primary method you use to trigger UI updates from event handlers and server request callbacks. +Merges nextState with the current state. This is the primary method you use to trigger UI updates from event handlers and server request callbacks. In addition, you can supply an optional callback function that is executed once `setState` is completed. **Note:** *NEVER* mutate `this.state` directly. As calling `setState()` afterwards may replace the mutation you made. Treat `this.state` as if it were immutable. **Note:** `setState()` does not immediately mutate `this.state` but creates a pending state transition. Accessing `this.state` after calling this method can potentially return the existing value. +**Note**: There is no guarantee of synchronous operation of calls to `setState` and calls may eventually be batched for performance gains. + #### replaceState ```javascript diff --git a/src/core/ReactCompositeComponent.js b/src/core/ReactCompositeComponent.js index 03f17019f1b..7a98769c0d0 100644 --- a/src/core/ReactCompositeComponent.js +++ b/src/core/ReactCompositeComponent.js @@ -538,8 +538,8 @@ var ReactCompositeComponentMixin = { setState: function(partialState, callback) { // Merge with `_pendingState` if it exists, otherwise with existing state. this.replaceState(merge(this._pendingState || this.state, partialState)); - // If `callback` is callable, do it. - typeof callback === 'function' && callback(); + // If `callback` is truthy, do it. + callback && callback(); }, /** From c81cc2e6d5dfe6f26ed1f5c971e162b62fcad709 Mon Sep 17 00:00:00 2001 From: ngavalas Date: Fri, 14 Jun 2013 16:41:02 -0700 Subject: [PATCH 3/4] markdown syntax Small problem with markdown syntax in syntax-highlighted block. --- docs/docs/api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/api.md b/docs/docs/api.md index cc73f2e3a37..1ae5e97b37f 100644 --- a/docs/docs/api.md +++ b/docs/docs/api.md @@ -108,7 +108,7 @@ Transfer properties from this component to a target component that have not alre #### setState ```javascript -setState(object nextState_[, function callback]_) +setState(object nextState[, function callback]) ``` Merges nextState with the current state. This is the primary method you use to trigger UI updates from event handlers and server request callbacks. In addition, you can supply an optional callback function that is executed once `setState` is completed. From 7a0f2d71bbfd6ea03eb239830855d992c1ecb841 Mon Sep 17 00:00:00 2001 From: ngavalas Date: Sun, 16 Jun 2013 22:45:36 -0700 Subject: [PATCH 4/4] Add callbacks to all public-facing state/props methods All public facing {set,replace,force}{props,state} methods now support callbacks. --- src/core/ReactComponent.js | 10 +++++++--- src/core/ReactCompositeComponent.js | 15 ++++++++++----- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/core/ReactComponent.js b/src/core/ReactComponent.js index b9aa2330c49..3353310b1f8 100644 --- a/src/core/ReactComponent.js +++ b/src/core/ReactComponent.js @@ -231,21 +231,23 @@ var ReactComponent = { * Sets a subset of the props. * * @param {object} partialProps Subset of the next props. + * @param {?function} callback Called after props are updated. * @final * @public */ - setProps: function(partialProps) { - this.replaceProps(merge(this.props, partialProps)); + setProps: function(partialProps, callback) { + this.replaceProps(merge(this.props, partialProps), callback); }, /** * Replaces all of the props. * * @param {object} props New props. + * @param {?function} callback Called after props are updated. * @final * @public */ - replaceProps: function(props) { + replaceProps: function(props, callback) { invariant( !this.props[OWNER], 'replaceProps(...): You called `setProps` or `replaceProps` on a ' + @@ -257,6 +259,8 @@ var ReactComponent = { var transaction = ReactComponent.ReactReconcileTransaction.getPooled(); transaction.perform(this.receiveProps, this, props, transaction); ReactComponent.ReactReconcileTransaction.release(transaction); + + callback && callback(); }, /** diff --git a/src/core/ReactCompositeComponent.js b/src/core/ReactCompositeComponent.js index 7a98769c0d0..78041c2a054 100644 --- a/src/core/ReactCompositeComponent.js +++ b/src/core/ReactCompositeComponent.js @@ -537,9 +537,7 @@ var ReactCompositeComponentMixin = { */ setState: function(partialState, callback) { // Merge with `_pendingState` if it exists, otherwise with existing state. - this.replaceState(merge(this._pendingState || this.state, partialState)); - // If `callback` is truthy, do it. - callback && callback(); + this.replaceState(merge(this._pendingState || this.state, partialState), callback); }, /** @@ -550,10 +548,11 @@ var ReactCompositeComponentMixin = { * accessing `this.state` after calling this method may return the old value. * * @param {object} completeState Next state. + * @param {?function} callback Called after state is updated. * @final * @protected */ - replaceState: function(completeState) { + replaceState: function(completeState, callback) { var compositeLifeCycleState = this._compositeLifeCycleState; invariant( this.isMounted() || @@ -590,6 +589,9 @@ var ReactCompositeComponentMixin = { this._compositeLifeCycleState = null; } + + // If callback is 'truthy', execute it + callback && callback(); }, /** @@ -712,10 +714,11 @@ var ReactCompositeComponentMixin = { * This will not invoke `shouldUpdateComponent`, but it will invoke * `componentWillUpdate` and `componentDidUpdate`. * + * @param {?function} callback Called after update is complete. * @final * @protected */ - forceUpdate: function() { + forceUpdate: function(callback) { var compositeLifeCycleState = this._compositeLifeCycleState; invariant( this.isMounted(), @@ -736,6 +739,8 @@ var ReactCompositeComponentMixin = { transaction ); ReactComponent.ReactReconcileTransaction.release(transaction); + + callback && callback(); }, /**