import { Action, Reactable, ReducerBuilder, SubReducer } from '../types' import { extractActionType, extractActionTypes } from '../utils/actions' interface ReducersMap { [key: string]: SubReducer[] } /** Creates a new ReducerBuilder. A ReducerBuilder is just a reducer with a special `.reducer()` method for easily mapping action types to sub-reducers that handle them. */ export const createReducer = (initialState?: State) => { const actionToReducersMap: ReducersMap = {} const reducer = ((state: State = initialState as State, action: Action) => { const reducers = actionToReducersMap[action.type] || [] return runReducers(reducers, state, action) }) as ReducerBuilder reducer.reduce = ( reactable: Reactable | Reactable[], subReducer: SubReducer ) => { const method = 'ReducerBuilder.reduce()' const actionTypes = Array.isArray(reactable) ? extractActionTypes(reactable, method) : [extractActionType(reactable, method)] mapActionTypesToReducer(actionToReducersMap, actionTypes, subReducer) return reducer } return reducer } const mapActionTypesToReducer = ( map: ReducersMap, actionTypes: string[], consumer: SubReducer ) => { actionTypes.forEach(actionType => { if (!map[actionType]) { map[actionType] = [] } map[actionType].push(consumer) }) } const runReducers = ( reducers: SubReducer[], state: State, action: Action ) => { return reducers.reduce( (accumulatedState, reducer) => reducer(accumulatedState, action.payload, action), state ) }