UNPKG

react-redux-methods

Version:

A lightweight react-redux toolkit for writing strong-typed, minimal code redux boilerplate.

124 lines (123 loc) 6.15 kB
"use strict"; 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();