Actions with TypeScript - rkaku/react-hooks-101 GitHub Wiki

  • actions
    • types
      • App.ts
    • creators
      • App.ts
    • reducers
      • App.ts

actions / types / App.ts

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 }

pages / App.tsx

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
⚠️ **GitHub.com Fallback** ⚠️