Also see React with Immer.

Immer based Reducer

const initialState = []
const reducer = produce((draft, action) => {
    switch (action.type) {
        case "my-action":
            // use draft in here.
            break
    }
})
 
const [state, dispatch] = useReducer(reducer, initialState);

Updating an existing useReducer

If you want to use immer for a new action, without rewriting an whole reducer:

case 'my-action': return produce(state, draft => {
  // apply changes
})

Even less boilerplate

If you don’t want to write anymore boilerplate and want to just focus on writing features, you can just use zustand instead (which also solves many other common pitfalls of redux / react)

import { create } from 'zustand'
import { produce } from 'immer'
 
const useLushStore = create((set) => ({
  lush: { forest: { contains: { a: 'bear' } } },
  clearForest: () =>
    set(
      produce((state) => {
        state.lush.forest.contains = null
      }),
    ),
}))
 
const clearForest = useLushStore((state) => state.clearForest)
clearForest()

It also integrates well with Contexts and Typescript (but doesn’t require complex / long action types)