redux-observable-sans
Version:
Use redux without any boilerplate
150 lines (119 loc) • 5.61 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.httpAction = exports.httpEpic = exports.createType = exports.rpcFailure = exports.rpcSuccess = exports.rpcRequest = undefined;
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; };
require('whatwg-fetch');
var _reduxObservable = require('redux-observable');
var _rxjs = require('rxjs');
var _operators = require('rxjs/operators');
function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
// fetch does not throw on anything but network errors
// thus status needs to be passed down along with body
var responseToJson = function responseToJson(response) {
return response.json().then(function (cleanJson) {
return {
code: response.status,
body: cleanJson
};
});
};
// check kind of response by code
// then return stream of success actions or stream of failure actions
// in case of 401, then failure stream includes action.type === `UNAUTHORIZED`
var mapResponseToSuccessOrFailureStream = function mapResponseToSuccessOrFailureStream(type, url, httpType) {
return function (response) {
console.error('response =', response);
if (response.code >= 200 && response.code < 300) {
return (0, _rxjs.from)([rpcSuccess(type)(response.body)]);
}
return (0, _rxjs.from)(rpcFailure(type, url, httpType)(response));
};
};
// action creators
var rpcRequest = exports.rpcRequest = function rpcRequest(type) {
return function (payload) {
return { type: type + '_REQUEST', payload: payload };
};
};
var rpcSuccess = exports.rpcSuccess = function rpcSuccess(type) {
return function (payload) {
return { type: type + '_SUCCESS', payload: payload };
};
};
var rpcFailure = exports.rpcFailure = function rpcFailure(type, url, httpType) {
return function (response) {
return [{
type: type + '_FAILURE',
payload: _extends({
error: true,
message: response.message
}, response && response.xhr ? response.xhr.response : response.body)
}, {
type: response && response.code === 401 ? httpType + '_UNAUTHORIZED' : httpType + '_ERROR',
payload: _extends({
url: url,
error: true,
message: response.message || response.body && response.body.message
}, response)
}];
};
};
var createType = exports.createType = function createType(id) {
return 'HTTP' + (id ? '_' + id : '');
};
// Race between the AJAX call and an EPIC_END.
// If the EPIC_END, emit a cancel action to
// put the store in the correct state
// This is needed for hot reloading
var httpEpic = function httpEpic() {
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
var preUrl = !options.url ? '/' : options.url[options.url.length] === '/' ? options.url : options.url + '/';
var setHeaders = typeof options.setHeaders === 'function' ? options.setHeaders : function () {
return {};
};
var httpType = createType(options.id);
return function (action$, state$, deps) {
return action$.pipe((0, _reduxObservable.ofType)(httpType), (0, _operators.mergeMap)(function (_ref) {
var payload = _ref.payload;
var rawBody = payload.body,
postUrl = payload.url,
type = payload.type,
settings = _objectWithoutProperties(payload, ['body', 'url', 'type']);
var headers = _extends({
Accept: 'application/json',
'Content-Type': 'application/json'
}, setHeaders(state$, deps));
var options = _extends({
headers: headers
}, settings);
var body = rawBody && typeof rawBody.toJS === 'function' ? rawBody.toJS() // handle immutable
: rawBody;
if (body && ['POST', 'PATCH', 'PUT'].some(function (method) {
return method === settings.method;
})) options.body = JSON.stringify(body);
var url = preUrl + (typeof postUrl === 'function' ? postUrl(payload.body) : postUrl || '');
var reqAction$ = (0, _rxjs.from)([rpcRequest(type)(_extends({ url: url }, settings, { body: body }))]);
console.error('options =', options);
var sideEffect$ = (0, _rxjs.from)(fetch(url, options).then(responseToJson)).pipe((0, _operators.mergeMap)(mapResponseToSuccessOrFailureStream(type, url, httpType)), (0, _operators.takeUntil)(action$.ofType(type + '_CANCELLED')), (0, _operators.catchError)(function (err) {
return (0, _rxjs.from)(rpcFailure(type, url, httpType)(err));
}));
var hotReloading$ = action$.pipe((0, _reduxObservable.ofType)('EPIC_END'), (0, _operators.mapTo)({ type: type + '_CANCELLED' }), (0, _operators.take)(1));
return (0, _rxjs.merge)(reqAction$, (0, _rxjs.race)(sideEffect$, hotReloading$));
}));
};
};
exports.httpEpic = httpEpic;
var httpAction = exports.httpAction = function httpAction(dispatch, id) {
return function (type, url) {
var method = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'GET';
var settings = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
return function (body) {
return dispatch({
type: createType(id),
payload: _extends({ type: type, url: url, method: method }, settings, { body: body })
});
};
};
};