Skip to content
D
Documentation

Update state with actions

how-to
2 min readUpdated

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:

bash
npm 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.

ts
import { 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.

tsx
import { 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.

OptionTypeDefaultWhat it does
Object passed to setPartial store stateNo default; supply an updateSets known field values and shallowly merges them into the existing object state.
Function passed to setCurrent state → complete or partial store stateNo default; supply an updateComputes the update from the current state, as inc does above.
Second argument to setbooleanMerges when omitted for object updatesfalse 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.

Was this page helpful?