Skip to content

Releases: pmndrs/zustand

Zustand 3.x

Choose a tag to compare

@drcmda drcmda released this 17 Aug 20:04

What's new

First of all, this release is 100% backward compatible, it will not break your code! The only breaking change (the create functions return value) still can work in the old way, but throws a small deprecation warning.

Our main focus has been:

  • Stability and fixing bugs
  • Making it suitable for concurrent mode
  • Supporting React-fast-refresh
  • Making it more versatile so that it can be used without React

We will soon publish an experimental V4 that utilizes react-experimentals useMutableSource in concurrent mode. The groundwork for this has been laid in this major.

Simpler API

import create from 'zustand'

const useStore = create(set => ({
  bears: 0,
  increasePopulation: () => set(state => ({ count: state.bears + 1 })),
  resetPopulation: () => set({ bears: 0 })
}))

function BearCounter() {
  const bears = useStore(state => state.bears)
  return <h1>{bears} around here ...</h1>
}

function Controls() {
  const increasePopulation = useStore(state => state.increasePopulation)
  return <button onClick={increasePopulation}>one up</button>
}

Utility functions are on the hooks prototype

Sometimes you need to access the state in a non-reactive way, or act upon the store. For these cases the resulting hook has utility functions attached to its prototype.

const useStore = create(() => ({ paw: true, snout: true, fur: true }))

// Getting non-reactive fresh state
const paw = useStore.getState().paw
// Listening to all changes, fires on every change
const unsub1 = useStore.subscribe(console.log)
// Listening to selected changes, in this case when "paw" changes
const unsub2 = useStore.subscribe(console.log, state => state.paw)
// Updating state, will trigger listeners
useStore.setState({ paw: false })
// Unsubscribe listeners
unsub1()
unsub2()
// Destroying the store (removing all listeners)
useStore.destroy()

// You can of course use the hook as you always would
function Component() {
  const paw = useStore(state => state.paw)

Using zustand without React

Zustands core can be imported and used without the React dependency. The only difference is that the create function does not return a hook, but the api utilities.

import create from 'zustand/vanilla'

const store = create(() => ({ ... }))
const { getState, setState, subscribe, destroy } = store

You can even consume an existing vanilla store with React:

import create from 'zustand'
import vanillaStore from './vanillaStore'

const useStore = create(vanillaStore)

Overwriting state

The set function has a second argument, false by default. Instead of merging, it will replace the state model. Be careful not to wipe out parts you rely on, like actions.

import omit from "lodash-es/omit"

const useStore = create(set => ({
  salmon: 1,
  tuna: 2,
  deleteEverything: () => set({ }), true), // clears the entire store, actions included
  deleteTuna: () => set(state => omit(state, ['tuna']), true)
}))

v2.2.0

Choose a tag to compare

@JeremyRH JeremyRH released this 24 Oct 14:15

2.2.0

v2.1.0

Choose a tag to compare

@JeremyRH JeremyRH released this 11 Oct 16:49
d82e103
  • Removed SubscribeOptions and UseStoreSubscribeOptions types.
  • Added Subscriber type.
  • Improve performance by consolidating subscribers sparse array.
  • Fix potential issue with concurrent mode.

v2.0.0

Choose a tag to compare

@JeremyRH JeremyRH released this 11 Oct 16:47
e77ab24
  • Removed ESM and IIFE versions of middleware.js and shallow.js. Any imports of the ".cjs" files should be changed from "zustand/{name}.cjs" to just "zustand/{name}" as they are all CommonJS now (index.js still has different formats).
  • api.subscribe has a new function signature that removes the object wrapper: api.subscribe(listener, selector?, equalityFn?).
  • StateListener type has changed. An overloaded signature was added for better error handling.
  • Subscribe type has changed. It uses the updated StateListener type.
  • listeners are iterated in a different order than they were before. Parent to child rather than child to parent. It would be rare if this causes any issues.
  • The automatic fix when passing an array as the second arg of useStore was removed. useStore will throw an Uncaught TypeError if the second arg is an array.