All files / src/app/store/reducers generic.reducer.ts

100% Statements 30/30
100% Branches 12/12
100% Functions 8/8
100% Lines 26/26
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53                    1x   11x     27x 45x 2x   104x 103x 29x   74x 74x 74x   11x     1x 74x 12x   62x 62x 62x 62x     1x   62x 36x 36x   26x   62x     1x  
import { Action, Reducer } from 'redux';
import { ActionWithSubState } from '../actions/actionWithSubState';
 
// tslint:disable-next-line:interface-over-type-literal
export type reducerFactoryConfig = {
  initialState: any,
  handledActions: string[],
  actionQualifierConfig: any
};
 
export class GenericReducerFactory {
 
  createReducer<T>(config: reducerFactoryConfig): Reducer<T> {
 
    const actionQualifier =
        typeof config.actionQualifierConfig === 'string' ? (action) => `[${config.actionQualifierConfig}] ${action.type}`
      : typeof config.actionQualifierConfig === 'function' ? (action) => config.actionQualifierConfig(action)
      : (action) => action.type;
 
    const genericReducer = (state: T = config.initialState, action: Action) => {
      if (config.handledActions.indexOf(action.type) === -1) {
        return state;
      }
      const newState = this.genericActionHandler(state, action);
      action.type = actionQualifier(action);
      return newState;
    };
    return genericReducer;
  }
 
  genericActionHandler(state, action) {
    if (!action.payload) {
      return state;
    }
    const newState = {...state};
    const subState = this.resolveSubstate(newState, action);
    Object.assign(subState, action.payload);
    return newState;
  }
 
  private resolveSubstate(newState, action) {
    let subState;
    if (action.subState) {
      subState = {...newState[action.subState]};
      newState[action.subState] = subState;
    } else {
      subState = newState;
    }
    return subState;
  }
 
}