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

Oldest comments (4)

Collapse
 
waju profile image
Abolarin Olanrewaju Olabode

This is really cool.

Collapse
 
acro5piano profile image
Kay Gosho

Thanks!

Collapse
 
mconnor profile image
mike connor

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

Collapse
 
acro5piano profile image
Kay Gosho

Thanks! I've fixed!