Just like Maps, always create a new Set instance when updating.
Add Item
Delete Item
Toggle Item
Clear All
// Add an itemset((state) => ({ bar: new Set(state.bar).add(item),}))
// Delete an itemset((state) => { const next = new Set(state.bar) next.delete(item) return { bar: next }})
// Toggle an item (add if not present, delete if present)set((state) => { const next = new Set(state.bar) next.has(item) ? next.delete(item) : next.add(item) return { bar: next }})
Zustand detects changes by comparing references using Object.is. Mutating a Map or Set in place doesn’t change its reference:
// This won't trigger a re-renderset((state) => { state.foo.set(key, value) return { foo: state.foo } // Same reference!})
When you create new Map(state.foo), it creates a shallow copy of the Map with a new reference. This new reference tells Zustand that the state has changed.
When initializing empty Maps and Sets, provide type hints to prevent TypeScript from inferring never[]:
interface State { ids: Set<string> users: Map<string, User>}const useStore = create<State>()((set) => ({ // Without type hints, TypeScript infers never[] - can't add items later // ids: new Set([]), // Bad! // users: new Map([]), // Bad! // With type hints, TypeScript knows the correct types ids: new Set([] as string[]), // Good! users: new Map([] as [string, User][]), // Good!}))
Without proper type hints, TypeScript will infer never[] which prevents adding items to the collection later.
Remember: The key principle is always create new instances when updating Maps and Sets. This ensures Zustand can detect changes and trigger re-renders appropriately.