DEV Community

Kay Gosho
Kay Gosho

Posted on • Updated on

useReducer in TypeScript, strictly typed version

https://reactjs.org/docs/hooks-reference.html#usereducer

interface Action<T extends string, P = undefined> {
  type: T
  payload: P
}

type Actions = 
  | Action<'increment', { by: number }>
  | Action<'decrement', { by: number }>
  | Action<'reset', { to: number }>

type Reducer<S, A extends Action<any, any>> = (s: S, a: A) => S

const INITIAL_STATE = {
  count: 0,
} as const

const reducer: Reducer<typeof INITIAL_STATE, Actions>(state, action) => {
  switch (action.type) {
    case 'increment':
      return { count: state.count + action.payload.by };
    case 'decrement':
      return { count: state.count - action.payload.by };
    case 'reset':
      return { count: action.payload.to };
    default:
      throw new Error();
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({ type: 'decrement', payload: { by: 1 })}>-</button>
      <button onClick={() => dispatch({ type: 'increment', payload: { by: 1 }})}>+</button>
      <button onClick={() => dispatch({ type: 'reset', payload: { to: 0 } })}>+</button>
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

Latest comments (4)

Collapse
 
mconnor profile image
mike connor

missing '=' before '(state, action) =>' ??

Collapse
 
acro5piano profile image
Kay Gosho

Thanks! I've fixed!

Collapse
 
waju profile image
Abolarin Olanrewaju Olabode

This is really cool.

Collapse
 
acro5piano profile image
Kay Gosho

Thanks!