diff --git a/docs/docs/ref-02-component-api.md b/docs/docs/ref-02-component-api.md index 2deeef703ed..1074d6f520b 100644 --- a/docs/docs/ref-02-component-api.md +++ b/docs/docs/ref-02-component-api.md @@ -17,7 +17,7 @@ Instances of a React Component are created internally in React when rendering. T 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 and the component is re-rendered. +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 and the component is re-rendered. If Promises/A+ are supported, the method also returns a promise. > Notes: > diff --git a/src/class/ReactClass.js b/src/class/ReactClass.js index b38bc4c72ce..3897e7bfeff 100644 --- a/src/class/ReactClass.js +++ b/src/class/ReactClass.js @@ -730,7 +730,7 @@ var ReactClassMixin = { internalInstance, 'setState(...): Can only update a mounted or mounting component.' ); - internalInstance.setState( + return internalInstance.setState( partialState, callback && callback.bind(this) ); }, @@ -745,7 +745,7 @@ var ReactClassMixin = { internalInstance, 'replaceState(...): Can only update a mounted or mounting component.' ); - internalInstance.replaceState( + return internalInstance.replaceState( newState, callback && callback.bind(this) ); @@ -772,7 +772,7 @@ var ReactClassMixin = { 'forceUpdate(...): Can only force an update on mounted or mounting ' + 'components.' ); - internalInstance.forceUpdate(callback && callback.bind(this)); + return internalInstance.forceUpdate(callback && callback.bind(this)); }, /** @@ -803,7 +803,7 @@ var ReactClassMixin = { internalInstance, 'setProps(...): Can only update a mounted component.' ); - internalInstance.setProps( + return internalInstance.setProps( partialProps, callback && callback.bind(this) ); @@ -819,7 +819,7 @@ var ReactClassMixin = { * @deprecated */ replaceProps: function(newProps, callback) { - ReactInstanceMap.get(this).replaceProps( + return ReactInstanceMap.get(this).replaceProps( newProps, callback && callback.bind(this) ); diff --git a/src/core/ReactCompositeComponent.js b/src/core/ReactCompositeComponent.js index c3d880ce713..5c6b597f38f 100644 --- a/src/core/ReactCompositeComponent.js +++ b/src/core/ReactCompositeComponent.js @@ -293,7 +293,7 @@ var ReactCompositeComponentMixin = assign({}, // Merge with the pending element if it exists, otherwise with existing // element props. var element = this._pendingElement || this._currentElement; - this.replaceProps( + return this.replaceProps( assign({}, element.props, partialProps), callback ); @@ -322,7 +322,7 @@ var ReactCompositeComponentMixin = assign({}, this._pendingElement || this._currentElement, props ); - ReactUpdates.enqueueUpdate(this, callback); + return ReactUpdates.enqueueUpdate(this, callback); }, /** @@ -341,7 +341,7 @@ var ReactCompositeComponentMixin = assign({}, element, assign({}, element.props, partialProps) ); - ReactUpdates.enqueueUpdate(this, callback); + return ReactUpdates.enqueueUpdate(this, callback); }, /** @@ -357,7 +357,7 @@ var ReactCompositeComponentMixin = assign({}, */ setState: function(partialState, callback) { // Merge with `_pendingState` if it exists, otherwise with existing state. - this.replaceState( + return this.replaceState( assign({}, this._pendingState || this._instance.state, partialState), callback ); @@ -385,7 +385,7 @@ var ReactCompositeComponentMixin = assign({}, // TODO: The callback here is ignored when setState is called from // componentWillMount. Either fix it or disallow doing so completely in // favor of getInitialState. - ReactUpdates.enqueueUpdate(this, callback); + return ReactUpdates.enqueueUpdate(this, callback); } }, @@ -412,7 +412,7 @@ var ReactCompositeComponentMixin = assign({}, 'or during an existing state transition (such as within `render`).' ); this._pendingForceUpdate = true; - ReactUpdates.enqueueUpdate(this, callback); + return ReactUpdates.enqueueUpdate(this, callback); }, /** diff --git a/src/core/ReactUpdates.js b/src/core/ReactUpdates.js index 601d6a268c8..f2b2aba513a 100644 --- a/src/core/ReactUpdates.js +++ b/src/core/ReactUpdates.js @@ -218,14 +218,29 @@ function enqueueUpdate(component, callback) { } dirtyComponents.push(component); + var resolver, promise; + + if (typeof Promise === 'function') { + promise = new Promise(function(resolve, reject) { + resolver = function() { + // Always resolve, there is no reason for Promise to be rejected. + if (typeof callback === 'function') { + callback(); + } + resolve(); + }; + }); + } - if (callback) { + if (resolver || callback) { if (component._pendingCallbacks) { - component._pendingCallbacks.push(callback); + component._pendingCallbacks.push(resolver || callback); } else { - component._pendingCallbacks = [callback]; + component._pendingCallbacks = [resolver || callback]; } } + + return promise; } /** diff --git a/src/core/__tests__/ReactUpdates-test.js b/src/core/__tests__/ReactUpdates-test.js index 052d61e25dc..13942556021 100644 --- a/src/core/__tests__/ReactUpdates-test.js +++ b/src/core/__tests__/ReactUpdates-test.js @@ -244,6 +244,45 @@ describe('ReactUpdates', function() { expect(updateCount).toBe(2); }); + it('should return a promise if Promises/A+ are supported', function() { + var updateCount = 0; + var Component = React.createClass({ + getInitialState: function() { + return {x: 0}; + }, + componentDidUpdate: function() { + updateCount++; + }, + render: function() { + return