Update a deep object property
A store contains objects nested several levels deep and one leaf needs changing without affecting sibling fields.
Zustand · Const
Zustand nested updates are immutable: copy each object along the path with spreads, then replace the target value. The set function shallowly merges the returned top-level object, so nested levels must be merged explicitly.
A nested update must preserve every object level between the store root and the changed value. Use the previous state in an updater function, spread each containing object, and replace only the targeted property; Immer, optics-ts, or Ramda can reduce this boilerplate.
set((state) => ({ nested: { ...state.nested, value: nextValue } }))The updater returns a partial state object containing replacement objects along the changed path.
set to submit the update; Zustand combines the returned top-level fields with the existing store.import { create } from 'zustand'
type Store = {
deep: { nested: { obj: { count: number } } }
increment: () => void
}
export const useStore = create<Store>((set) => ({
deep: { nested: { obj: { count: 0 } } },
increment: () =>
set((state) => ({
deep: {
...state.deep,
nested: {
...state.deep.nested,
obj: {
...state.deep.nested.obj,
count: state.deep.nested.obj.count + 1,
},
},
},
})),
}))The action copies every containing object before changing the deeply nested count, preserving immutable updates.
A store contains objects nested several levels deep and one leaf needs changing without affecting sibling fields.
A counter, form field, or other leaf value depends on its current nested value.
Manual spreads become difficult to maintain as the state shape grows deeper.
Use an updater function with set, then copy each object from the root to the changed property using spreads. Replace the leaf value while retaining sibling properties.
Follow-up: What does Zustand merge automatically? Only the top-level state object; nested objects require explicit merging.
The default set behavior shallowly merges the returned object. It does not recursively merge nested objects, so omitting a nested sibling can lose that data.
Follow-up: How could you shorten repetitive nested copying? Use the documented Immer, optics-ts, or Ramda approaches.
Immer lets the update use mutation-like syntax while producing an immutable result. The documented pattern wraps the state creator with the immer middleware and updates the draft inside set.
Follow-up: What tradeoff does the manual spread approach avoid? It avoids adding a helper library, at the cost of more boilerplate.
setImmeroptics-tsRamdaimmutable state and merging