export = {
INCREMENT: 'INCREMENT',
SET_COUNT: 'SET_COUNT'
} as const
actions / creators / App.ts
import types from '../types/App'
export const increment = () => {
return { type: types.INCREMENT }
}
export const setCount = (amount: number) => {
return { type: types.SET_COUNT, payload: { amount } }
}
types / creatorsToActions.ts
type ReturnTypes<T> = {
[K in keyof T]: T[K] extends (...args: any[]) => any ? ReturnType<T[K]> : never
}
type Unbox<T> = T extends { [K in keyof T]: infer U } ? U : never
export type creatorsToActions<T> = Unbox<ReturnTypes<T>>
actions / reducers / App.ts
import types from '../types/App'
import * as creators from '../creators/App'
import { creatorsToActions } from '../../types/creatorsToActions'
type State = {
count: number,
unit: string
}
type Actions = creatorsToActions<typeof creators>
function initialState(injects?: Partial<State>): State {
return { count: 0, unit: 'pt', ...injects }
}
function reducer(state: State, action: Actions): State {
switch (action.type) {
case types.INCREMENT:
return { ...state, count: state.count + 1 }
case types.SET_COUNT:
return { ...state, count: action.payload.amount }
default:
throw new Error()
}
}
export { reducer, initialState }
import React, { useReducer, useMemo, useCallback } from 'react'
import { reducer, initialState } from '../actions/reducers/App'
import { increment, setCount } from '../actions/creators/App'
type Props = {
countLabel: string,
onClickIncrement: () => void,
reset: () => void
}
function Component: React.FC<Props>(props) {
return (
<>
Count: { props.countLabel }
<button onClick={ props.onClickIncrement }>+1</button>
<button onClick={ props.reset }>:)</button>
</>
)
}
function Container: React.FC() {
const [state, dispatch] = useReducer(reducer, initialState({ count: 0 })
const countLabel = useMemo(() => `${ state.count }`, [state])
const onClickIncrement = useCallback(() => dispatch(increment()), [dispatch])
const reset = useCallback(() => dispatch(setCount(0), [dispatch])
return (
<Component
countLabel={ countLabel }
onClickIncrement={ onClickIncrement }
reset={ reset }
/>
export default Container