redux-requests
Version:
Manages in-flight requests with a Redux reducer - avoid issuing duplicate requests without any special logic!
100 lines (89 loc) • 4.21 kB
JavaScript
/**
* Reducer function to handle pending requests.
* @param {Object} state Existing state object.
* @param {Object} action Incoming action:
* - Ations with the meta.httpRequest property are examined.
* - The meta.httpRequest.url property is added or removed
* from the current state depending on if the meta.httpRequest.done
* property is set.
* @return {Object} The new state.
*/
;
Object.defineProperty(exports, "__esModule", {
value: true
});
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; };
exports.requestsReducer = requestsReducer;
exports.createRequestMiddleware = createRequestMiddleware;
exports.attemptRequest = attemptRequest;
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function requestsReducer(state, action) {
if (state === undefined) state = {};
if (!action.meta || !action.meta.httpRequest || !action.meta.httpRequest.url) {
return state;
}
if (action.meta.httpRequest.done) {
// Remove this request from the state
var newState = _extends({}, state);
delete newState[action.meta.httpRequest.url];
return newState;
} else {
// Add this request to the state
return _extends({}, state, _defineProperty({}, action.meta.httpRequest.url, true));
}
}
/**
* Creates a Redux middleware function when called.
* @param {Function} selectorFunc A function to select the location in the store's state tree where
* the requests reducer keeps it's state.
* @return {Function} A middleware function that will only dispatch the action if the
* action.meta.httpRequest.done property is false, and the
* meta.httpRequest.url is not already in flight.
*/
function createRequestMiddleware() {
var selectorFunc = arguments.length <= 0 || arguments[0] === undefined ? function (state) {
return state.requests;
} : arguments[0];
return function (store) {
return function (next) {
return function (action) {
// Cancel HTTP request if there is already one pending for this URL
if (action.meta && action.meta.httpRequest && !action.meta.httpRequest.done) {
var requests = selectorFunc(store.getState());
if (requests[action.meta.httpRequest.url]) {
// There is a request for this URL in flight already!
// (Ignore the action)
return;
}
}
return next(action);
};
};
};
}
/**
* Helper function to attempt a request and handle the response.
* @param {String} url The URL the request is for.
* @param {Object} actions Actions to dispatch depending on the outcome of the "makeRequest" Promise.
* @param {Function} makeRequest Function that returns a Promise object. This function performs the actual request.
* @param {Function} dispatch Redux store dispatch function.
*/
function attemptRequest(url, actions, makeRequest, dispatch) {
var beginAction = _extends({}, actions.begin());
beginAction.meta = beginAction.meta || {};
beginAction.meta.httpRequest = { url: url, done: false };
if (!dispatch(beginAction)) {
return; // bail out here if the middleware cancelled the dispatch
}
makeRequest().then(function (response) {
var successAction = _extends({}, actions.success(response));
successAction.meta = successAction.meta || {};
successAction.meta.httpRequest = { url: url, done: true };
dispatch(successAction);
})["catch"](function (err) {
var failureAction = _extends({}, actions.failure(err));
failureAction.meta = failureAction.meta || {};
failureAction.meta.httpRequest = { url: url, done: true };
dispatch(failureAction);
});
}