Refactor to allow dispatching of two kinds of Actions

They are:
 1. The existing type of Action, Objects with an `action` type.
 1. Asyncronous Actions, functions that accept a `dispatch` argument, which can be used to dispatch Actions asyncronously.
This commit is contained in:
Luke Barnard 2017-12-12 17:32:43 +00:00
parent 60d8ebb914
commit 13925db251
5 changed files with 37 additions and 31 deletions

View file

@ -14,13 +14,12 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import { createPromiseActionCreator } from './actionCreators'; import { asyncAction } from './actionCreators';
const GroupActions = {}; const GroupActions = {};
GroupActions.fetchJoinedGroups = createPromiseActionCreator( GroupActions.fetchJoinedGroups = function(matrixClient) {
'GroupActions.fetchJoinedGroups', return asyncAction('GroupActions.fetchJoinedGroups', () => matrixClient.getJoinedGroups());
(matrixClient) => matrixClient.getJoinedGroups(), };
);
export default GroupActions; export default GroupActions;

View file

@ -15,14 +15,13 @@ limitations under the License.
*/ */
import Analytics from '../Analytics'; import Analytics from '../Analytics';
import { createPromiseActionCreator } from './actionCreators'; import { asyncAction } from './actionCreators';
import TagOrderStore from '../stores/TagOrderStore'; import TagOrderStore from '../stores/TagOrderStore';
const TagOrderActions = {}; const TagOrderActions = {};
TagOrderActions.commitTagOrdering = createPromiseActionCreator( TagOrderActions.commitTagOrdering = function(matrixClient) {
'TagOrderActions.commitTagOrdering', return asyncAction('TagOrderActions.commitTagOrdering', () => {
(matrixClient) => {
// Only commit tags if the state is ready, i.e. not null // Only commit tags if the state is ready, i.e. not null
const tags = TagOrderStore.getOrderedTags(); const tags = TagOrderStore.getOrderedTags();
if (!tags) { if (!tags) {
@ -31,7 +30,7 @@ TagOrderActions.commitTagOrdering = createPromiseActionCreator(
Analytics.trackEvent('TagOrderActions', 'commitTagOrdering'); Analytics.trackEvent('TagOrderActions', 'commitTagOrdering');
return matrixClient.setAccountData('im.vector.web.tag_ordering', {tags}); return matrixClient.setAccountData('im.vector.web.tag_ordering', {tags});
}, });
); };
export default TagOrderActions; export default TagOrderActions;

View file

@ -14,25 +14,23 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import dis from '../dispatcher';
/** /**
* Create an action creator that will dispatch actions asynchronously that * Create an asynchronous action creator that will dispatch actions indicating
* indicate the current status of promise returned by the given function, fn. * the current status of the promise returned by fn.
* @param {string} id the id to give the dispatched actions. This is given a * @param {string} id the id to give the dispatched actions. This is given a
* suffix determining whether it is pending, successful or * suffix determining whether it is pending, successful or
* a failure. * a failure.
* @param {function} fn the function to call with arguments given to the * @param {function} fn a function that returns a Promise.
* returned function. This function should return a Promise. * @returns {function} a function that uses its single argument as a dispatch
* @returns {function} a function that dispatches asynchronous actions when called. * function.
*/ */
export function createPromiseActionCreator(id, fn) { export function asyncAction(id, fn) {
return (...args) => { return (dispatch) => {
dis.dispatch({action: id + '.pending'}); dispatch({action: id + '.pending'});
fn(...args).then((result) => { fn().then((result) => {
dis.dispatch({action: id + '.success', result}); dispatch({action: id + '.success', result});
}).catch((err) => { }).catch((err) => {
dis.dispatch({action: id + '.failure', err}); dispatch({action: id + '.failure', err});
}); });
}; };
} }

View file

@ -68,7 +68,7 @@ const TagPanel = React.createClass({
}); });
}); });
// This could be done by anything with a matrix client // This could be done by anything with a matrix client
GroupActions.fetchJoinedGroups(this.context.matrixClient); dis.dispatch(GroupActions.fetchJoinedGroups(this.context.matrixClient));
}, },
componentWillUnmount() { componentWillUnmount() {
@ -81,7 +81,7 @@ const TagPanel = React.createClass({
_onGroupMyMembership() { _onGroupMyMembership() {
if (this.unmounted) return; if (this.unmounted) return;
GroupActions.fetchJoinedGroups(this.context.matrixClient); dis.dispatch(GroupActions.fetchJoinedGroups.bind(this.context.matrixClient));
}, },
onClick() { onClick() {
@ -94,7 +94,7 @@ const TagPanel = React.createClass({
}, },
onTagTileEndDrag() { onTagTileEndDrag() {
TagOrderActions.commitTagOrdering(this.context.matrixClient); dis.dispatch(TagOrderActions.commitTagOrdering(this.context.matrixClient));
}, },
render() { render() {

View file

@ -20,14 +20,24 @@ const flux = require("flux");
class MatrixDispatcher extends flux.Dispatcher { class MatrixDispatcher extends flux.Dispatcher {
/** /**
* @param {Object} payload Required. The payload to dispatch. * @param {Object|function} payload Required. The payload to dispatch.
* Must contain at least an 'action' key. * If an Object, must contain at least an 'action' key.
* If a function, must have the signature (dispatch) => {...}.
* @param {boolean=} sync Optional. Pass true to dispatch * @param {boolean=} sync Optional. Pass true to dispatch
* synchronously. This is useful for anything triggering * synchronously. This is useful for anything triggering
* an operation that the browser requires user interaction * an operation that the browser requires user interaction
* for. * for.
*/ */
dispatch(payload, sync) { dispatch(payload, sync) {
// Allow for asynchronous dispatching by accepting payloads that have the
// type `function (dispatch) {...}`
if (typeof payload === 'function') {
payload((action) => {
this.dispatch(action, sync);
});
return;
}
if (sync) { if (sync) {
super.dispatch(payload); super.dispatch(payload);
} else { } else {