react-redux-methods
Version:
A lightweight react-redux toolkit for writing strong-typed, minimal code redux boilerplate.
124 lines (123 loc) • 6.15 kB
JavaScript
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
if (ar || !(i in from)) {
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
ar[i] = from[i];
}
}
return to.concat(ar || Array.prototype.slice.call(from));
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createReduxMethods = void 0;
var reselect_1 = require("reselect");
var get = require("lodash.get");
/**
* A function that returns a tuple ['reducer','actions', 'stateSelections'] based on the provided required parameters including
* 'initialState', and state mutation 'reducers'.
*
* @param initialState Accepts any kind of value, such as object, number, string, null, except undefined.
*
* @param reducers Accepts an object of functions (or reducer) used in mutating state. Each function has two arguments -- 'State' or previous
* state, and optional 'Payload'. For example, (state) => state or (state, action) => state. When providing second argument 'payload'
* 'ComposePayload' type must be used as utility type. For example, (state: State, action: ComposePayload\<string\>) => ({...state, firstName: action.payload}).
*
* @param selectionNode A string that must be provided when making state selections partnered with 'selectors' parameter. The format of
* this string should be in dot notation as how the case state node is located in the redux state. For example, root
* state 'communications.chats'.
*
* @param selectors An object of selection functions that uses 'createSelector' function of 'reselect' package under the hood.
* Partnered with 'selectionNode' parameter, each case selection can accept multiple arguments but the first argument is restricted
* for the result of 'selectionNode' which represents the current state of the node. For example, (state, prop) => state.currentUser.id = prop.
* In most cases, the single argument pattern, for example (state) => state.currentUser, is the common selection pattern.
*
* Under the hood, each selection is converted to a standard redux selection pattern that starts from the root node of the redux state.
* In this case, the actual selection is (reduxState) => reduxState.communications.chats.currentUser.
*
* @returns A tuple as ['reducer','actions', 'stateSelections']. In a 'reducer', it is a function that invokes every reducer inside the passed
* object, and builds a state object with the same shape, while in 'actions' are the transformed reducers that optionally
* accepts a 'payload' based on the second parameter of each case reducer. If action is provided, then the consumer react
* component should provide a single argument "payload". The 'stateSelections' are the transformed case selections conformed with
* redux selection pattern such as (reduxState) => reduxState.communications.chats.currentUser.
*/
var createReduxMethods = function (_a) {
var initialState = _a.initialState, reducers = _a.reducers, selectionNode = _a.selectionNode, selectors = _a.selectors;
// compose a reducer function
var reducer = function (state, action) {
if (state === void 0) { state = initialState; }
var type = action.type.replace("".concat(selectionNode, "/"), '');
if (reducers.hasOwnProperty(type)) {
return reducers[type](state, __assign(__assign({}, action), { plainType: type }));
}
else {
return state;
}
};
// compose actions for react components to dispatch
var actions = Object.keys(reducers).reduce(function (acc, key) {
var _a, _b;
var type = "".concat(selectionNode, "/").concat(key);
var reducerMeta = reducers[key];
if (reducerMeta.length === 2) {
return __assign(__assign({}, acc), (_a = {}, _a[key] = function (payload) { return ({ type: type, payload: payload }); }, _a));
}
else {
return __assign(__assign({}, acc), (_b = {}, _b[key] = function () { return ({ type: type }); }, _b));
}
}, {});
// compose selectors
var selections = {};
if (selectionNode && selectors) {
selections = Object.keys(selectors).reduce(function (acc, key) {
var _a;
return (__assign(__assign({}, acc), (_a = {}, _a[key] = (0, reselect_1.createSelector)(function (state) {
var ownProps = [];
for (var _i = 1; _i < arguments.length; _i++) {
ownProps[_i - 1] = arguments[_i];
}
return ({
nodeState: get(state, selectionNode),
ownProps: ownProps,
});
}, // get starting point state (node state)
function (_a) {
var nodeState = _a.nodeState, ownProps = _a.ownProps;
return selectors[key].apply(selectors, __spreadArray([nodeState], ownProps, false));
}), _a)));
}, {});
}
return [reducer, actions, selections];
};
exports.createReduxMethods = createReduxMethods;
// *************************** EXAMPLE USE CASE *********************************
// type StateShape = {
// isOk: boolean;
// canFly: boolean;
// };
// const initialState: StateShape = {
// isOk: true,
// canFly: false,
// };
// const [reducer, actions] = createReduxMethods({
// initialState,
// reducers: {
// makeFly: (s, a: CreatePayload<{ canFly: boolean }>) => ({
// ...s,
// ...a.payload,
// }),
// canMake: (s) => ({ ...s }),
// },
// });
// const data = actions.makeFly({ canFly: true });
// const data1 = actions.canMake();
;