Skip to content

Commit

Permalink
feat: add reducer factory
Browse files Browse the repository at this point in the history
  • Loading branch information
Mohammad Hasani committed Jan 18, 2019
1 parent 279e517 commit b170555
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 0 deletions.
46 changes: 46 additions & 0 deletions src/__tests__/create-reducer.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { createReducer } from '../create-reducer'
import { createAction } from '../create-action'

describe('createReducer', () => {
const increment = createAction('[Counter] increment')
const decrement = createAction('[Counter] decrement')
const reset = createAction('[Counter] reset', resolve => (value: number) =>
resolve(value)
)

const handleIncrement = jest.fn((state: number) => state + 1)
const handleDecrement = jest.fn((state: number) => state - 1)
const handleReset = jest.fn(
(_: number, { payload }: ReturnType<typeof reset>) => payload
)

const defaultState = 0
const counter = createReducer(defaultState, handle => [
handle(increment, handleIncrement),
handle(decrement, handleDecrement),
handle(reset, handleReset),
])

beforeEach(() => {
handleIncrement.mockReset()
handleDecrement.mockReset()
handleReset.mockReset()
})

it('should initiate with default state when state is undefined', () => {
expect(counter(undefined, increment())).toBe(
counter(defaultState, increment())
)
})

it('should pass through state when there is no proper handler', () => {
expect(counter(defaultState, { type: 'NOT DEFINED' })).toBe(defaultState)
})

it('should calls related handler of the given action', () => {
expect(counter(defaultState, increment)).toBe(handleIncrement(defaultState))
expect(handleIncrement).toBeCalledTimes(2)
expect(handleDecrement).not.toBeCalled()
expect(handleReset).not.toBeCalled()
})
})
31 changes: 31 additions & 0 deletions src/create-reducer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import {
createHandlerMap,
CreateHandlerMap,
HandlerMap,
} from './create-handler-map'
import { merge } from './utils'

/**
* Reducer factory
* @description combines multiple handler map into single reducer
* @example
* const counter = createReducer(0, handle => [
* handle(increment, state => state + 1),
* handle(decrement, state => state - 1),
* ])
*/
export function createReducer<State, HM extends HandlerMap<State, any>>(
defaultState: State,
handlerMapsCreator: (handle: CreateHandlerMap<State>) => HM[]
) {
const handlerMap = merge(...handlerMapsCreator(createHandlerMap))

return (
state = defaultState,
action: HM extends HandlerMap<State, infer T> ? T : never
) => {
const handler = handlerMap[action.type]

return handler ? handler(state, action) : state
}
}
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export { action, Action, AnyAction } from './action'
export { createAction, ActionCreator } from './create-action'
export { getType } from './get-type'
export { createReducer } from './create-reducer'
1 change: 1 addition & 0 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './object'
2 changes: 2 additions & 0 deletions src/utils/object.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const merge = <T extends {}>(...objs: T[]): T =>
Object.assign({}, ...objs)

0 comments on commit b170555

Please sign in to comment.