redux-tiles
Version:
Library to create and easily compose redux pieces together in less verbose manner
76 lines (67 loc) • 2.56 kB
text/typescript
import { forEachRight, get, isFunction, mapValues } from 'lodash';
import { Action, Reducer } from 'redux';
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[];
// index stays as it was in original array, so the first
// element in the iteration has `i` of the last element!
forEachRight(path, (el: string, i: number) => {
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
};
}