redux-tiles
Version:
Library to create and easily compose redux pieces together in less verbose manner
84 lines (75 loc) • 2.53 kB
text/typescript
import { Action, Reducer } from "redux";
import { get, isFunction, mapValues } from "../utils";
import { ReducerObject } from "./types";
/**
* @overview create reducer function from the object
* @param {Any} initialState – initial state of this part of the store
* @param {Object} handlers – object with keys as action types, and
* reduce functions to change store as values
* @return {Function} – function to act as a reducer
*/
export function createReducerFromObject(
initialState: any,
handlers: ReducerObject
): Reducer<any> {
return function reducer(state: {} = initialState, action: Action): {} {
const handler: Function | {} = handlers[action.type];
return typeof handler === "function" ? handler(state, action) : state;
};
}
/**
* @overview create reducer from the object, with creating reducing functions
* @param {Any} initialState – initial state of this part of the store
* @param {Object} handlers – object with keys as action types, and
* newValues to set at store as values
* @return {Function} – function to act as a reducer
*/
export function createReducer(
initialState: any,
handlers: ReducerObject
): Reducer<any> {
return createReducerFromObject(
initialState,
mapValues(handlers, (value: any) => (state: any, action: Action): any =>
reducerCreator({
state,
action,
newValue: isFunction(value) ? value(state, action) : value
})
)
);
}
/**
* @overview reducer function, which changes the state with new values
* @param {Object} action – reducer action object, with type and payload
* @param {Object} state – previous redux state in this branch
* @param {Any} newValue – new value to set up in state in corresponding path
* @return {Object} – changed reducer
*/
export function reducerCreator({ action, state, newValue }: any): any {
const { path } = action.payload;
const hasNoNestInStore: boolean = !path;
if (hasNoNestInStore) {
return newValue;
}
let result: any = {};
let lookupPath: string[];
const length: number = path.length;
for (let i: number = length - 1; i >= 0; i = i - 1) {
const el: string = path[i];
const isLastItem: boolean = i === path.length - 1;
const newNestedResult: any = {
[el]: isLastItem ? newValue : result
};
lookupPath = path.slice(0, i);
const oldState: any = get(state, lookupPath) || {};
result = {
...oldState,
...newNestedResult
};
}
return {
...state,
...result
};
}