Use colocated actions when React controls need to update shared state. This example displays a count starting at 0; the +1 and +5 buttons increase it from its previous value by the chosen amount.
Create the store with create, then select the state and actions each component needs. The returned store is a hook, so you do not need a provider. Keep update functions alongside state rather than introducing dispatched actions or reducers for this task.
1. Define a typed action
In your React TypeScript project, add counter-store.ts. If you have not installed Zustand, run:
bashnpm install zustand
Extend the React quick start pattern with an action argument: inc accepts a numeric amount and returns void. Use a functional update because the next count depends on the current store state.
tsimport { create } from 'zustand'
type CounterStore = {
count: number
inc: (by: number) => void
}
export const useCounterStore = create<CounterStore>()((set) => ({
count: 0,
inc: (by) => set((state) => ({ count: state.count + by })),
}))
set calls the updater with the current state, shallowly merges the returned object into the store, and notifies listeners. Returning only { count: ... } preserves inc; you do not need to spread the whole store into this flat update.
2. Bind the action to a React control
Use this browser entry point with an HTML element whose ID is root, as in the React quick start. Keep the starter's separate selectors, and wrap each button handler to pass the amount to inc rather than the click event.
tsximport { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import { useCounterStore } from './counter-store'
function Counter() {
const count = useCounterStore((state) => state.count)
const inc = useCounterStore((state) => state.inc)
return (
<main style={{ minHeight: 200, padding: 24 }}>
<p>Count: {count}</p>
<button type="button" onClick={() => inc(1)}>
+1
</button>
<button type="button" onClick={() => inc(5)}>
+5
</button>
</main>
)
}
createRoot(document.getElementById('root')!).render(
<StrictMode>
<Counter />
</StrictMode>,
)
The mounted component displays Count: 0 and +1 and +5 buttons. Clicking either button calls inc with its amount, changes the selected count, and updates the displayed number. Click +1, then +5: the displayed count changes to 1, then 6. The action returns no value; read the result through the count selector.
Update choices
Choose the update form according to where the next value comes from.
| Option | Type | Default | What it does |
|---|---|---|---|
Object passed to set | Partial store state | No default; supply an update | Sets known field values and shallowly merges them into the existing object state. |
Function passed to set | Current state → complete or partial store state | No default; supply an update | Computes the update from the current state, as inc does above. |
Second argument to set | boolean | Merges when omitted for object updates | false merges; true replaces the state. See replacement typing and precautions. |
Pitfalls
- Keep the previous-value calculation inside the functional updater so it reads the store value at the time of the call.
- For nested objects, follow Update nested state and the merging rules in State and actions.
- To select several values as one object or array, follow Selectors and subscriptions. This sample selects the count and action separately.
Live demo
Try the live counter demo. It uses the same functional increment pattern as the sample's +1 button. Browse the demo source or the typed React starter for the original examples.
Related
- State and actions explains immutable updates and merging.
- Split a store into slices keeps colocated state and actions modular as the store grows.
- Use reducers and dispatch covers the alternative reducer pattern.
Was this page helpful?