Handle store updates through a reducer and dispatched actions when a reducer-based action pattern fits your application.
Use this pattern when you want one reducer to describe how explicit action objects change a store. Zustand does not require reducers or dispatched actions; its recommended pattern is to keep actions with state and update through set or setState.
Create a reducer-backed store
The redux middleware takes a reducer and initial state, then returns a state creator with dispatch. Wrap it with create to get a React store hook.
- Define the state and action union. Each action has a string
typeand any data the reducer needs. - Write a pure reducer that takes the current state and action and returns the next state.
- Pass the reducer and initial state to
redux, then create a hook-backed store. The selecteddispatchfunction accepts only the declared action union.
tsximport { create } from 'zustand'
import { redux } from 'zustand/middleware'
import { createRoot } from 'react-dom/client'
type AlertsState = {
alertsEnabled: boolean
}
type AlertsAction =
| { type: 'alerts/enable' }
| { type: 'alerts/disable' }
type AlertsStore = AlertsState & {
dispatch: (action: AlertsAction) => AlertsAction
}
function alertsReducer(
state: AlertsState,
action: AlertsAction,
): AlertsState {
switch (action.type) {
case 'alerts/enable':
return { alertsEnabled: true }
case 'alerts/disable':
return { alertsEnabled: false }
default:
return state
}
}
const initialState: AlertsState = { alertsEnabled: false }
const useAlertsStore = create<AlertsStore>()(
redux(alertsReducer, initialState),
)
function AlertsSetting() {
const alertsEnabled = useAlertsStore((state) => state.alertsEnabled)
const dispatch = useAlertsStore((state) => state.dispatch)
return (
<section>
<p>Alerts: {alertsEnabled ? 'On' : 'Off'}</p>
<button onClick={() => dispatch({ type: 'alerts/enable' })}>
Enable alerts
</button>
<button onClick={() => dispatch({ type: 'alerts/disable' })}>
Disable alerts
</button>
</section>
)
}
const container = document.createElement('div')
document.body.append(container)
createRoot(container).render(<AlertsSetting />)
The mounted control starts at Alerts: Off. Clicking Enable alerts dispatches its action and changes the display to Alerts: On; Disable alerts dispatches the action that displays Alerts: Off.
Reducer and middleware inputs
redux has no configuration options here. It takes these two required inputs:
| Input | Type | Default | Effect |
|---|---|---|---|
reducer | (state: T, action: A) => T | None | Computes the next state from the current state and an action. Keep it pure. |
initialState | T | None | Provides the store's starting state; it cannot be a function. |
The middleware returns a state creator. Its dispatch function is available in the store state and on the store API, and calling it returns the dispatched action.
Pitfalls
- Reducers and dispatched actions are optional in Zustand. For the colocated-action pattern without a reducer, see Create and bind a React store.
See it running
Try the Zustand live demo.
Was this page helpful?