# Use reducers and dispatch

Use the shipped [`redux`](https://bench-zustand-61.atloria.app/p/bench-zustand-61-pSJCWrWW5Y/developer/zustand-middleware#redux) middleware when you want typed action objects and a reducer to drive store updates. This example renders a person form: editing a field dispatches an action and updates the displayed name and email.

Reducers are optional. Zustand's recommended Flux-inspired pattern colocates state and update functions without requiring dispatch; see [Update state with actions](https://bench-zustand-61.atloria.app/p/bench-zustand-61-pSJCWrWW5Y/developer/update-state-with-actions) for that approach.

## 1. Create the reducer-backed store

In your React project, install Zustand:

```bash
npm install zustand
```

Define your application state and a discriminated union of actions. Each action has a string `type` and the payload its reducer branch needs. Pass the reducer and initial state to `redux`, then pass the resulting state creator to [`create`](https://bench-zustand-61.atloria.app/p/bench-zustand-61-pSJCWrWW5Y/developer/zustand#create).

```ts title="person-store.ts"
import { create } from 'zustand'
import { redux } from 'zustand/middleware'

type PersonState = {
  firstName: string
  lastName: string
  email: string
}

type PersonAction =
  | { type: 'person/setFirstName'; firstName: string }
  | { type: 'person/setLastName'; lastName: string }
  | { type: 'person/setEmail'; email: string }

function personReducer(state: PersonState, action: PersonAction): PersonState {
  switch (action.type) {
    case 'person/setFirstName':
      return { ...state, firstName: action.firstName }
    case 'person/setLastName':
      return { ...state, lastName: action.lastName }
    case 'person/setEmail':
      return { ...state, email: action.email }
    default:
      return state
  }
}

const initialState: PersonState = {
  firstName: 'Barbara',
  lastName: 'Hepworth',
  email: 'bhepworth@sculpture.com',
}

export const usePersonStore = create(redux(personReducer, initialState))
```

The result is a store hook with the initial person data and a typed `dispatch` function. The middleware adds `dispatch` both to the state and to the store API; you do not need to implement it yourself.

## 2. Render the form and dispatch from its controls

Use this browser entry point in a React project whose HTML contains `<div id="root"></div>`. Select each field and `dispatch` separately, following the starter's selector pattern. No provider is required.

```tsx title="main.tsx"
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import { usePersonStore } from './person-store'

function PersonForm() {
  const firstName = usePersonStore((state) => state.firstName)
  const lastName = usePersonStore((state) => state.lastName)
  const email = usePersonStore((state) => state.email)
  const dispatch = usePersonStore((state) => state.dispatch)

  return (
    <main style={{ minHeight: 400, padding: 24 }}>
      <h1>Edit person</h1>
      <label style={{ display: 'block' }}>
        First name:
        <input
          value={firstName}
          onChange={(event) =>
            dispatch({
              type: 'person/setFirstName',
              firstName: event.currentTarget.value,
            })
          }
        />
      </label>
      <label style={{ display: 'block' }}>
        Last name:
        <input
          value={lastName}
          onChange={(event) =>
            dispatch({
              type: 'person/setLastName',
              lastName: event.currentTarget.value,
            })
          }
        />
      </label>
      <label style={{ display: 'block' }}>
        Email:
        <input
          type="email"
          value={email}
          onChange={(event) =>
            dispatch({
              type: 'person/setEmail',
              email: event.currentTarget.value,
            })
          }
        />
      </label>
      <p>{firstName} {lastName} ({email})</p>
    </main>
  )
}

const container = document.getElementById('root')!
createRoot(container).render(
  <StrictMode>
    <PersonForm />
  </StrictMode>,
)
```

The form starts with Barbara Hepworth's name and email. Edit any input: `dispatch` passes the current state and action to the reducer, and the selected field and summary update. Each reducer branch copies the state and changes only its target field.

`dispatch(action)` returns the action you passed, not the new state. Outside React, you can call `usePersonStore.dispatch(action)` on this same store; read the current state with `usePersonStore.getState()`.

## Parameters that matter

`redux` takes two required arguments, not an options object:

| Argument | Type | Default | What it does |
| --- | --- | --- | --- |
| `reducer` | `(state: T, action: A) => T` | Required | Computes the next state from the current state and action. `A` must extend `{ type: string }`. |
| `initialState` | `T` | Required | Supplies the initial state alongside the middleware's `dispatch` function. |

## Pitfalls

- Keep the reducer pure: return the next state rather than mutating the current state. Put side effects in functions that wrap dispatch, not in the reducer.
- Reserve the `dispatch` field for the middleware. Its initialization spreads your initial state after adding `dispatch`, so an initial-state field with that name overrides the state-level function.
- Middleware composition has typing and ordering constraints. See [Type stores and middleware](https://bench-zustand-61.atloria.app/p/bench-zustand-61-pSJCWrWW5Y/developer/type-stores-and-middleware) when adding more middleware to this store.

## Live demo and related pages

Try the [Zustand live demo](https://zustand-demo.pmnd.rs/) to see a rendered counter driven by a store hook. It demonstrates hook-based updates, rather than this reducer-backed person form.

- [React quick start](https://bench-zustand-61.atloria.app/p/bench-zustand-61-pSJCWrWW5Y/developer/react-quick-start) — Set up a mounted React store.
- [Selectors and subscriptions](https://bench-zustand-61.atloria.app/p/bench-zustand-61-pSJCWrWW5Y/developer/selectors-and-subscriptions) — Choose which state each component observes.
- [Debug store updates](https://bench-zustand-61.atloria.app/p/bench-zustand-61-pSJCWrWW5Y/developer/debug-store-updates) — Inspect updates with Redux DevTools.
