AK-21/Graphite-Industrial-Intelligence
0
1<p align="center">2 <img src="bear.jpg" />3</p>4 5[](https://github.com/pmndrs/zustand/actions?query=workflow%3ALint)6[](https://bundlephobia.com/result?p=zustand)7[](https://www.npmjs.com/package/zustand)8[](https://www.npmjs.com/package/zustand)9[](https://discord.gg/poimandres)10 11A small, fast and scalable bearbones state-management solution using simplified flux principles. Has a comfy API based on hooks, isn't boilerplatey or opinionated.12 13Don't disregard it because it's cute. It has quite the claws, lots of time was spent dealing with common pitfalls, like the dreaded [zombie child problem](https://react-redux.js.org/api/hooks#stale-props-and-zombie-children), [react concurrency](https://github.com/bvaughn/rfcs/blob/useMutableSource/text/0000-use-mutable-source.md), and [context loss](https://github.com/facebook/react/issues/13332) between mixed renderers. It may be the one state-manager in the React space that gets all of these right.14 15You can try a live demo [here](https://githubbox.com/pmndrs/zustand/tree/main/examples/demo).16 17```bash18npm i zustand19```20 21:warning: This readme is written for JavaScript users. If you are a TypeScript user, be sure to check out our [TypeScript Usage section](#typescript-usage).22 23## First create a store24 25Your store is a hook! You can put anything in it: primitives, objects, functions. State has to be updated immutably and the `set` function [merges state](./docs/guides/immutable-state-and-merging.md) to help it.26 27```jsx28import { create } from 'zustand'29 30const useBearStore = create((set) => ({31 bears: 0,32 increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),33 removeAllBears: () => set({ bears: 0 }),34}))35```36 37## Then bind your components, and that's it!38 39Use the hook anywhere, no providers are needed. Select your state and the component will re-render on changes.40 41```jsx42function BearCounter() {43 const bears = useBearStore((state) => state.bears)44 return <h1>{bears} around here ...</h1>45}46 47function Controls() {48 const increasePopulation = useBearStore((state) => state.increasePopulation)49 return <button onClick={increasePopulation}>one up</button>50}51```52 53### Why zustand over redux?54 55- Simple and un-opinionated56- Makes hooks the primary means of consuming state57- Doesn't wrap your app in context providers58- [Can inform components transiently (without causing render)](#transient-updates-for-often-occurring-state-changes)59 60### Why zustand over context?61 62- Less boilerplate63- Renders components only on changes64- Centralized, action-based state management65 66---67 68# Recipes69 70## Fetching everything71 72You can, but bear in mind that it will cause the component to update on every state change!73 74```jsx75const state = useBearStore()76```77 78## Selecting multiple state slices79 80It detects changes with strict-equality (old === new) by default, this is efficient for atomic state picks.81 82```jsx83const nuts = useBearStore((state) => state.nuts)84const honey = useBearStore((state) => state.honey)85```86 87If you want to construct a single object with multiple state-picks inside, similar to redux's mapStateToProps, you can use [useShallow](./docs/guides/prevent-rerenders-with-use-shallow.md) to prevent unnecessary rerenders when the selector output does not change according to shallow equal.88 89```jsx90import { create } from 'zustand'91import { useShallow } from 'zustand/react/shallow'92 93const useBearStore = create((set) => ({94 bears: 0,95 increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),96 removeAllBears: () => set({ bears: 0 }),97}))98 99// Object pick, re-renders the component when either state.nuts or state.honey change100const { nuts, honey } = useBearStore(101 useShallow((state) => ({ nuts: state.nuts, honey: state.honey })),102)103 104// Array pick, re-renders the component when either state.nuts or state.honey change105const [nuts, honey] = useBearStore(106 useShallow((state) => [state.nuts, state.honey]),107)108 109// Mapped picks, re-renders the component when state.treats changes in order, count or keys110const treats = useBearStore(useShallow((state) => Object.keys(state.treats)))111```112 113For more control over re-rendering, you may provide any custom equality function.114 115```jsx116const treats = useBearStore(117 (state) => state.treats,118 (oldTreats, newTreats) => compare(oldTreats, newTreats),119)120```121 122## Overwriting state123 124The `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.125 126```jsx127import omit from 'lodash-es/omit'128 129const useFishStore = create((set) => ({130 salmon: 1,131 tuna: 2,132 deleteEverything: () => set({}, true), // clears the entire store, actions included133 deleteTuna: () => set((state) => omit(state, ['tuna']), true),134}))135```136 137## Async actions138 139Just call `set` when you're ready, zustand doesn't care if your actions are async or not.140 141```jsx142const useFishStore = create((set) => ({143 fishies: {},144 fetch: async (pond) => {145 const response = await fetch(pond)146 set({ fishies: await response.json() })147 },148}))149```150 151## Read from state in actions152 153`set` allows fn-updates `set(state => result)`, but you still have access to state outside of it through `get`.154 155```jsx156const useSoundStore = create((set, get) => ({157 sound: 'grunt',158 action: () => {159 const sound = get().sound160 ...161```162 163## Reading/writing state and reacting to changes outside of components164 165Sometimes you need to access state in a non-reactive way or act upon the store. For these cases, the resulting hook has utility functions attached to its prototype.166 167:warning: This technique is not recommended for adding state in [React Server Components](https://github.com/reactjs/rfcs/blob/main/text/0188-server-components.md) (typically in Next.js 13 and above). It can lead to unexpected bugs and privacy issues for your users. For more details, see [#2200](https://github.com/pmndrs/zustand/discussions/2200).168 169```jsx170const useDogStore = create(() => ({ paw: true, snout: true, fur: true }))171 172// Getting non-reactive fresh state173const paw = useDogStore.getState().paw174// Listening to all changes, fires synchronously on every change175const unsub1 = useDogStore.subscribe(console.log)176// Updating state, will trigger listeners177useDogStore.setState({ paw: false })178// Unsubscribe listeners179unsub1()180 181// You can of course use the hook as you always would182function Component() {183 const paw = useDogStore((state) => state.paw)184 ...185```186 187### Using subscribe with selector188 189If you need to subscribe with a selector,190`subscribeWithSelector` middleware will help.191 192With this middleware `subscribe` accepts an additional signature:193 194```ts195subscribe(selector, callback, options?: { equalityFn, fireImmediately }): Unsubscribe196```197 198```js199import { subscribeWithSelector } from 'zustand/middleware'200const useDogStore = create(201 subscribeWithSelector(() => ({ paw: true, snout: true, fur: true })),202)203 204// Listening to selected changes, in this case when "paw" changes205const unsub2 = useDogStore.subscribe((state) => state.paw, console.log)206// Subscribe also exposes the previous value207const unsub3 = useDogStore.subscribe(208 (state) => state.paw,209 (paw, previousPaw) => console.log(paw, previousPaw),210)211// Subscribe also supports an optional equality function212const unsub4 = useDogStore.subscribe(213 (state) => [state.paw, state.fur],214 console.log,215 { equalityFn: shallow },216)217// Subscribe and fire immediately218const unsub5 = useDogStore.subscribe((state) => state.paw, console.log, {219 fireImmediately: true,220})221```222 223## Using zustand without React224 225Zustand 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.226 227```jsx228import { createStore } from 'zustand/vanilla'229 230const store = createStore((set) => ...)231const { getState, setState, subscribe, getInitialState } = store232 233export default store234```235 236You can use a vanilla store with `useStore` hook available since v4.237 238```jsx239import { useStore } from 'zustand'240import { vanillaStore } from './vanillaStore'241 242const useBoundStore = (selector) => useStore(vanillaStore, selector)243```244 245:warning: Note that middlewares that modify `set` or `get` are not applied to `getState` and `setState`.246 247## Transient updates (for often occurring state-changes)248 249The subscribe function allows components to bind to a state-portion without forcing re-render on changes. Best combine it with useEffect for automatic unsubscribe on unmount. This can make a [drastic](https://codesandbox.io/s/peaceful-johnson-txtws) performance impact when you are allowed to mutate the view directly.250 251```jsx252const useScratchStore = create((set) => ({ scratches: 0, ... }))253 254const Component = () => {255 // Fetch initial state256 const scratchRef = useRef(useScratchStore.getState().scratches)257 // Connect to the store on mount, disconnect on unmount, catch state-changes in a reference258 useEffect(() => useScratchStore.subscribe(259 state => (scratchRef.current = state.scratches)260 ), [])261 ...262```263 264## Sick of reducers and changing nested states? Use Immer!265 266Reducing nested structures is tiresome. Have you tried [immer](https://github.com/mweststrate/immer)?267 268```jsx269import { produce } from 'immer'270 271const useLushStore = create((set) => ({272 lush: { forest: { contains: { a: 'bear' } } },273 clearForest: () =>274 set(275 produce((state) => {276 state.lush.forest.contains = null277 }),278 ),279}))280 281const clearForest = useLushStore((state) => state.clearForest)282clearForest()283```284 285[Alternatively, there are some other solutions.](./docs/guides/updating-state.md#with-immer)286 287## Persist middleware288 289You can persist your store's data using any kind of storage.290 291```jsx292import { create } from 'zustand'293import { persist, createJSONStorage } from 'zustand/middleware'294 295const useFishStore = create(296 persist(297 (set, get) => ({298 fishes: 0,299 addAFish: () => set({ fishes: get().fishes + 1 }),300 }),301 {302 name: 'food-storage', // name of the item in the storage (must be unique)303 storage: createJSONStorage(() => sessionStorage), // (optional) by default, 'localStorage' is used304 },305 ),306)307```308 309[See the full documentation for this middleware.](./docs/integrations/persisting-store-data.md)310 311## Immer middleware312 313Immer is available as middleware too.314 315```jsx316import { create } from 'zustand'317import { immer } from 'zustand/middleware/immer'318 319const useBeeStore = create(320 immer((set) => ({321 bees: 0,322 addBees: (by) =>323 set((state) => {324 state.bees += by325 }),326 })),327)328```329 330## Can't live without redux-like reducers and action types?331 332```jsx333const types = { increase: 'INCREASE', decrease: 'DECREASE' }334 335const reducer = (state, { type, by = 1 }) => {336 switch (type) {337 case types.increase:338 return { grumpiness: state.grumpiness + by }339 case types.decrease:340 return { grumpiness: state.grumpiness - by }341 }342}343 344const useGrumpyStore = create((set) => ({345 grumpiness: 0,346 dispatch: (args) => set((state) => reducer(state, args)),347}))348 349const dispatch = useGrumpyStore((state) => state.dispatch)350dispatch({ type: types.increase, by: 2 })351```352 353Or, just use our redux-middleware. It wires up your main-reducer, sets the initial state, and adds a dispatch function to the state itself and the vanilla API.354 355```jsx356import { redux } from 'zustand/middleware'357 358const useGrumpyStore = create(redux(reducer, initialState))359```360 361## Redux devtools362 363```jsx364import { devtools } from 'zustand/middleware'365 366// Usage with a plain action store, it will log actions as "setState"367const usePlainStore = create(devtools((set) => ...))368// Usage with a redux store, it will log full action types369const useReduxStore = create(devtools(redux(reducer, initialState)))370```371 372One redux devtools connection for multiple stores373 374```jsx375import { devtools } from 'zustand/middleware'376 377// Usage with a plain action store, it will log actions as "setState"378const usePlainStore1 = create(devtools((set) => ..., { name, store: storeName1 }))379const usePlainStore2 = create(devtools((set) => ..., { name, store: storeName2 }))380// Usage with a redux store, it will log full action types381const useReduxStore = create(devtools(redux(reducer, initialState)), , { name, store: storeName3 })382const useReduxStore = create(devtools(redux(reducer, initialState)), , { name, store: storeName4 })383```384 385Assigning different connection names will separate stores in redux devtools. This also helps group different stores into separate redux devtools connections.386 387devtools takes the store function as its first argument, optionally you can name the store or configure [serialize](https://github.com/zalmoxisus/redux-devtools-extension/blob/master/docs/API/Arguments.md#serialize) options with a second argument.388 389Name store: `devtools(..., {name: "MyStore"})`, which will create a separate instance named "MyStore" in the devtools.390 391Serialize options: `devtools(..., { serialize: { options: true } })`.392 393#### Logging Actions394 395devtools will only log actions from each separated store unlike in a typical _combined reducers_ redux store. See an approach to combining stores https://github.com/pmndrs/zustand/issues/163396 397You can log a specific action type for each `set` function by passing a third parameter:398 399```jsx400const useBearStore = create(devtools((set) => ({401 ...402 eatFish: () => set(403 (prev) => ({ fishes: prev.fishes > 1 ? prev.fishes - 1 : 0 }),404 undefined,405 'bear/eatFish'406 ),407 ...408```409 410You can also log the action's type along with its payload:411 412```jsx413 ...414 addFishes: (count) => set(415 (prev) => ({ fishes: prev.fishes + count }),416 undefined,417 { type: 'bear/addFishes', count, }418 ),419 ...420```421 422If an action type is not provided, it is defaulted to "anonymous". You can customize this default value by providing an `anonymousActionType` parameter:423 424```jsx425devtools(..., { anonymousActionType: 'unknown', ... })426```427 428If you wish to disable devtools (on production for instance). You can customize this setting by providing the `enabled` parameter:429 430```jsx431devtools(..., { enabled: false, ... })432```433 434## React context435 436The store created with `create` doesn't require context providers. In some cases, you may want to use contexts for dependency injection or if you want to initialize your store with props from a component. Because the normal store is a hook, passing it as a normal context value may violate the rules of hooks.437 438The recommended method available since v4 is to use the vanilla store.439 440```jsx441import { createContext, useContext } from 'react'442import { createStore, useStore } from 'zustand'443 444const store = createStore(...) // vanilla store without hooks445 446const StoreContext = createContext()447 448const App = () => (449 <StoreContext.Provider value={store}>450 ...451 </StoreContext.Provider>452)453 454const Component = () => {455 const store = useContext(StoreContext)456 const slice = useStore(store, selector)457 ...458```459 460## TypeScript Usage461 462Basic typescript usage doesn't require anything special except for writing `create<State>()(...)` instead of `create(...)`...463 464```ts465import { create } from 'zustand'466import { devtools, persist } from 'zustand/middleware'467import type {} from '@redux-devtools/extension' // required for devtools typing468 469interface BearState {470 bears: number471 increase: (by: number) => void472}473 474const useBearStore = create<BearState>()(475 devtools(476 persist(477 (set) => ({478 bears: 0,479 increase: (by) => set((state) => ({ bears: state.bears + by })),480 }),481 {482 name: 'bear-storage',483 },484 ),485 ),486)487```488 489A more complete TypeScript guide is [here](docs/guides/typescript.md).490 491## Best practices492 493- You may wonder how to organize your code for better maintenance: [Splitting the store into separate slices](./docs/guides/slices-pattern.md).494- Recommended usage for this unopinionated library: [Flux inspired practice](./docs/guides/flux-inspired-practice.md).495- [Calling actions outside a React event handler in pre-React 18](./docs/guides/event-handler-in-pre-react-18.md).496- [Testing](./docs/guides/testing.md)497- For more, have a look [in the docs folder](./docs/)498 499## Third-Party Libraries500 501Some users may want to extend Zustand's feature set which can be done using third-party libraries made by the community. For information regarding third-party libraries with Zustand, visit [the doc](./docs/integrations/third-party-libraries.md).502 503## Comparison with other libraries504 505- [Difference between zustand and other state management libraries for React](https://docs.pmnd.rs/zustand/getting-started/comparison)506 