Created
January 24, 2020 07:19
-
-
Save justinobney/9e8f411a1e7a6f0fcc95b89978230142 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import produce, {Draft} from 'immer'; | |
import {useReducer, useMemo} from 'react'; | |
type ActionCreator<State, Payload> = ( | |
payload: Payload | |
) => {type: string; payload: Payload}; | |
type CreatedAction<State, Payload> = ActionCreator<State, Payload> & AnyAction; | |
type Handler<State, Payload> = (state: Draft<State>, payload: Payload) => void; | |
type Handlers<State> = { | |
[actionType: string]: Handler<State, any>; | |
}; | |
interface AnyAction { | |
type: any; | |
} | |
interface Reducer<State> { | |
initialState: State; | |
when: <Payload>( | |
action: CreatedAction<State, Payload>, | |
handler: Handler<State, Payload> | |
) => Reducer<State>; | |
build: () => Handlers<State>; | |
} | |
export const actionCreatorFactory = <State>() => <Payload>(key: string) => { | |
const cb = (payload: Payload) => ({type: key, payload}); | |
((cb as unknown) as AnyAction).type = key; | |
return (cb as unknown) as CreatedAction<State, Payload>; | |
}; | |
export const createReducer = <State>(initialState?: State) => { | |
const reducer: Reducer<State> = { | |
initialState, | |
} as Reducer<State>; | |
const handlers: Handlers<State> = {}; | |
reducer.when = <Payload>( | |
action: CreatedAction<State, Payload>, | |
handler: Handler<State, Payload> | |
) => { | |
handlers[action.type] = handler; | |
return reducer; | |
}; | |
reducer.build = () => { | |
const buildReducer = Object.keys(handlers).reduce((acc, key) => { | |
acc[key] = (state, payload) => | |
produce(state, draft => { | |
handlers[key](draft, payload); | |
}); | |
return acc; | |
}, {}); | |
return buildReducer; | |
}; | |
return reducer; | |
}; | |
export const useImmerReducer = <State>( | |
reducer: Reducer<State>, | |
initialState?: State | |
) => { | |
const handlers = useMemo(() => reducer.build(), [reducer]); | |
return useReducer((state, action) => { | |
if (handlers[action.type]) { | |
return handlers[action.type](state, action.payload); | |
} | |
return state; | |
}, initialState ?? reducer.initialState); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment