redux-easy-async
Version:
Redux Easy Async makes handling asynchronous actions, such as API requests, simple, reliable, and powerful
133 lines (117 loc) • 5.94 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.createAsyncAction = undefined;
var _assign2 = require('lodash/assign');
var _assign3 = _interopRequireDefault(_assign2);
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
var _asyncConstants = require('./async-constants');
var _constants = require('./lib/constants');
var _action = require('./action');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var decorateActionCreator = function decorateActionCreator(actionCreator, asyncConstants) {
var START_TYPE = asyncConstants.START_TYPE,
SUCCESS_TYPE = asyncConstants.SUCCESS_TYPE,
FAIL_TYPE = asyncConstants.FAIL_TYPE,
NAME = asyncConstants.NAME;
(0, _assign3.default)(actionCreator, {
START_TYPE: START_TYPE,
SUCCESS_TYPE: SUCCESS_TYPE,
FAIL_TYPE: FAIL_TYPE,
NAME: NAME,
start: (0, _action.createAction)(START_TYPE),
success: (0, _action.createAction)(SUCCESS_TYPE),
fail: (0, _action.createAction)(FAIL_TYPE),
actionName: NAME
});
};
/**
*
* @kind function
* @param {(string|object)} type - can either be a string (e.g. "GET_POSTS") or a
* a constants object created with {@link createAsyncConstants}.
* @param {Function} fn - action creator function that returns an object with action configuration.
* See example below for configuration options. Only `makeRequest is required`.
* @param {Object} [options] additional configuration options
* @param {Object} [options.namespace=REDUX_EASY_ASYNC_NAMESPACE] the middleware action
* type this action will be dispatched with. You most likely don't want to modify this unless for
* some reason you want multiple instances of [async middleware]{@link createAsyncMiddleware}.
* @return {function} actionCreator
* @example <caption>All configuration options for async action</caption>
* import { createAsyncAction } from 'redux-easy-async';
*
* const myAction = createAsyncAction('MY_ACTION', () => {
* return {
* // function that makes the actual request. Return value must be a promise. In this example
* // `fetch()` returns a promise. **REQUIRED**
* makeRequest: () => fetch('/api/posts'),
*
* // *OPTIONAL*
* // additional meta that will be passed to the start, success, and fail actions if any. All
* // actions will have the following meta:
* // - `actionName`
* // - `asyncType`("start", "success", or "fail")
* // - `requestStartTime`
* // - `asyncID`: an unique id for each request
* // Success and fail actions will additionally have:
* // - `requestDuration`
* // - `resp`: the raw api response. Because of the nature of the promises errors that
* // cause the makeRequest promise to be rejected will also get caught here as `resp`
* // and cause a failed request action.
* meta: {},
*
* // function that takes your redux state and returns true or false whether to proceed with
* // the request. For example: checking if there is already a similar request in progress or
* // the requested data is already cached. *OPTIONAL*
* shouldMakeRequest: (state) => true,
*
* // `parseStart`, `parseSuccess`, and `parseFail` can be useful if you want to modify
* // raw API responses, errors, etc. before passing them to your reducer. The return value
* // of each becomes the payload for start, success, and fail actions. By default response
* // will not be modified.
* //
* // the return value of `parseStart` becomes the payload for the start action. *OPTIONAL*
* parseStart: () => null,
* // the return value of `parseSuccess` becomes the payload for the success action. *OPTIONAL*
* parseSuccess: resp => resp,
* // the return value of `parseFail` becomes the payload for the fail action. *OPTIONAL*
* parseFail: resp => resp,
* }
*
* })
*/
var createAsyncAction = exports.createAsyncAction = function createAsyncAction(type, fn) {
var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
var _options$namespace = options.namespace,
namespace = _options$namespace === undefined ? _constants.REDUX_EASY_ASYNC_NAMESPACE : _options$namespace;
var asyncConstants = (0, _asyncConstants.getOrCreateAsyncConstants)(type);
if (!asyncConstants) {
throw new Error('createAsyncAction(type, fn, options): ' + _constants.ERRORS.ASYNC_TYPE_NOT_VALID);
}
var actionCreator = function actionCreator() {
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
var action = fn.apply(undefined, args);
if ((typeof action === 'undefined' ? 'undefined' : _typeof(action)) !== 'object') {
throw new Error(_constants.ERRORS.ACTION_NOT_OBJECT);
}
if (typeof action.makeRequest !== 'function') {
throw new Error(_constants.ERRORS.MAKE_REQUEST_NOT_FUNCTION);
}
return _extends({
type: namespace
}, action, {
actionCreatorArgs: args,
actionName: actionCreator.actionName,
startActionCreator: actionCreator.start,
successActionCreator: actionCreator.success,
failActionCreator: actionCreator.fail
});
};
// attach the name and start, success, and fail actions for convenience
decorateActionCreator(actionCreator, asyncConstants);
return actionCreator;
};