@rootstrap/redux-tools
Version:
Redux tools we use in both react bases
215 lines (180 loc) • 5.35 kB
JavaScript
Object.defineProperty(exports, '__esModule', { value: true });
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
var imm = _interopDefault(require('immer'));
var reactRedux = require('react-redux');
/**
* Makes action creators
*
* @param {string} type - Dispatched actions type
* @return {function} Action creator
*
* @example
* const loginSuccess = createAction('LOGIN_SUCCESS')
*/
var createAction = type => {
const action = payload => ({
type,
payload,
});
action.toString = () => type;
return action
};
const NOT_STARTED = 'NOT_STARTED';
const LOADING = 'LOADING';
const SUCCESS = 'SUCCESS';
const ERROR = 'ERROR';
const REQUEST = 'REQUEST';
const RESET = 'RESET';
/**
* Creates different actions creators
*
* @param {string} actionName - Action name, will be used as a prefix for the action creators.
* @param {function} thunk - This is your async thunk, receives all forwarded params and `dispatch` and `getState` as params
*
* @returns {ActionCreator} Action that can be dispatched to start the async thunk, can also be
* deconstructed to get request, error, and success action creators (can be used as keys in reducer)
*
* @example
* export const getProfile = createActionWithThunk(
* 'LOGIN',
* user => userService.login(user),
* );
* export const { success, error } = getProfile;
*/
var createThunk = (actionName, thunk) => {
const request = createAction(`${actionName}_${REQUEST}`);
const error = createAction(`${actionName}_${ERROR}`);
const success = createAction(`${actionName}_${SUCCESS}`);
const reset = createAction(`${actionName}_${RESET}`);
const action = (...params) => ({
success,
error,
thunk: (dispatch, getState) => thunk(...params, dispatch, getState),
type: request.toString(),
});
action.request = request;
action.error = error;
action.success = success;
action.reset = reset;
action.toString = () => actionName;
return action
};
/**
* Reducer creator util
*
* @param initialState Reducer initial state
* @param actionHandlers - An object with all the reducer handlers
*
* @return {function} A reducer ready to use in createStore
*
* @example
* const myReducer = createReducer({}, {
* [loginSuccess]: (state, action) => {
* state.user = action.payload
* }
* })
*/
var createReducer = (initialState, actionHandlers) => (
state = initialState,
action,
) =>
imm(state, draft =>
actionHandlers[action.type]
? actionHandlers[action.type](draft, action)
: state,
);
const handleAction = (state, action) => {
const { type, payload } = action;
const matchesStart = /(.*)_REQUEST/.exec(type);
const matchesError = /(.*)_ERROR/.exec(type);
const matchesReset = /(.*)_RESET/.exec(type);
const matchesSuccess = /(.*)_SUCCESS/.exec(type);
let status = NOT_STARTED;
let key = null;
if (matchesStart) {
const [, requestName] = matchesStart;
key = requestName;
status = LOADING;
} else if (matchesReset) {
const [, requestName] = matchesReset;
key = requestName;
status = NOT_STARTED;
} else if (matchesError) {
const [, requestName] = matchesError;
key = requestName;
status = ERROR;
} else if (matchesSuccess) {
const [, requestName] = matchesSuccess;
key = requestName;
status = SUCCESS;
}
if (key) state[key] = { status, error: matchesError ? payload : undefined };
return state
};
var statusReducer = (state = {}, action) =>
imm(state, draft => handleAction(draft, action));
const id = item => item;
const thunkMiddlewareCreator = ({
parseError = id,
parseResponse = id,
} = {}) => ({ dispatch, getState }) => next => async action => {
next(action);
const { thunk, success, error } = action;
if (typeof thunk === 'function') {
try {
const response = await thunk(dispatch, getState);
return dispatch(success(parseResponse(response)))
} catch (err) {
return dispatch(error(parseError(err)))
}
}
};
const thunkMiddleware = thunkMiddlewareCreator();
thunkMiddleware.withConfig = thunkMiddlewareCreator;
/**
* useStatus hook
*
* @param {string} action Prefix for the action names
*
* @returns {object} Object with status and error keys
*
* @example
* const { status, error } = useStatus(login)
*/
var useStatus = action =>
reactRedux.useSelector(({ statusReducer }) => {
const { status, error } = statusReducer[action] || {};
return {
status,
error,
}
});
/**
* useLoading hook
*
* @param {string} action Prefix for the action names
*
* @returns {boolean} Wether the action is loading
*
* @example
* const isLoading = useStatus(getProfile)
*/
var useLoading = action =>
reactRedux.useSelector(({ statusReducer }) => {
const { status } = statusReducer[action] || {};
return status === LOADING
});
exports.ERROR = ERROR;
exports.LOADING = LOADING;
exports.NOT_STARTED = NOT_STARTED;
exports.REQUEST = REQUEST;
exports.RESET = RESET;
exports.SUCCESS = SUCCESS;
exports.createAction = createAction;
exports.createReducer = createReducer;
exports.createThunk = createThunk;
exports.statusReducer = statusReducer;
exports.thunkMiddleware = thunkMiddleware;
exports.useLoading = useLoading;
exports.useStatus = useStatus;
;