### Install zustand-computed Source: https://github.com/chrisvander/zustand-computed/blob/main/README.md Install the middleware using npm, pnpm, bun, or yarn. ```bash npm i zustand-computed pnpm i zustand-computed bun add zustand-computed yarn add zustand-computed ``` -------------------------------- ### Accessing Computed Values in Store Actions via get() Source: https://context7.com/chrisvander/zustand-computed/llms.txt Shows how computed properties are merged into the store, making them accessible via `get()` within other actions. This enables action logic that depends on derived state without manual recomputation. ```typescript import { create } from "zustand" import { createComputed } from "zustand-computed" type Store = { count: number inc: () => void squareIt: () => void // sets count to its current square sqrtIt: () => void // sets count to its current square root } type ComputedStore = { countSq: number countSqrt: number } const computed = createComputed((state: Store): ComputedStore => ({ countSq: state.count ** 2, countSqrt: Math.floor(Math.sqrt(state.count)), })) const useStore = create()( computed((set, get) => ({ count: 9, inc: () => set((s) => ({ count: s.count + 1 })), squareIt: () => set(() => ({ count: get().countSq })), // uses computed sqrtIt: () => set(() => ({ count: get().countSqrt })), // uses computed })) ) console.log(useStore.getState().count) // 9 console.log(useStore.getState().countSq) // 81 useStore.getState().sqrtIt() console.log(useStore.getState().count) // 3 useStore.getState().squareIt() console.log(useStore.getState().count) // 9 ``` -------------------------------- ### Integration with Immer Middleware Source: https://github.com/chrisvander/zustand-computed/blob/main/README.md Combine `zustand-computed` with other Zustand middleware like Immer. This example shows how to handle state updates within Immer's draft state. ```typescript const computed = createComputed((state: Store) => { /* ... */ }) const useStore = create()( devtools( immer( computed( (set) => ({ count: 1, inc: () => set((state) => { // example with Immer middleware state.count += 1 }), dec: () => set((state) => ({ count: state.count - 1 })), }) ) ) ) ) ``` -------------------------------- ### Custom Equality with `equalityFn` Source: https://context7.com/chrisvander/zustand-computed/llms.txt Provide a custom `equalityFn` to control how computed values are compared. This example uses `fast-deep-equal` to ensure the computed `stats` object reference only changes when its content differs, preventing unnecessary re-renders. ```typescript import { create } from "zustand" import { createComputed } from "zustand-computed" import deepEqual from "fast-deep-equal" // any deep-equality library type Store = { items: number[]; add: (n: number) => void } type Computed = { stats: { sum: number; avg: number } } const computed = createComputed( (state: Store): Computed => { const sum = state.items.reduce((a, b) => a + b, 0) return { stats: { sum, avg: sum / (state.items.length || 1) } } }, { // Use deep equality so `stats` reference only changes when content differs equalityFn: deepEqual, } ) const useStore = create()( computed((set) => ({ items: [1, 2, 3], add: (n) => set((s) => ({ items: [...s.items, n] })), })) ) const refBefore = useStore.getState().stats useStore.setState({ items: [1, 2, 3] }) // same content const refAfter = useStore.getState().stats console.log(refBefore === refAfter) // true — deep-equal prevented reference change ``` -------------------------------- ### Basic Usage with Computed State Source: https://github.com/chrisvander/zustand-computed/blob/main/README.md Integrate the `createComputed` middleware into your Zustand store to add computed properties. The compute function transforms the state, and the `get()` function within the store actions has access to these computed properties. ```javascript import { createComputed } from "zustand-computed" const computed = createComputed((state) => ({ countSq: state.count ** 2, })) const useStore = create( computed( (set, get) => ({ count: 1, inc: () => set((state) => ({ count: state.count + 1 })), dec: () => set((state) => ({ count: state.count - 1 })), // get() function has access to ComputedStore square: () => set(() => ({ count: get().countSq })), root: () => set((state) => ({ count: Math.floor(Math.sqrt(state.count)) })), }) ) ) ``` -------------------------------- ### Custom Recomputation with `shouldRecompute` Source: https://context7.com/chrisvander/zustand-computed/llms.txt Use `shouldRecompute` to define a custom predicate for when computed state should be recalculated. It receives previous and next states, returning true to recompute. This example ensures recomputation only occurs if the 'count' value has changed. ```typescript import { create } from "zustand" import { createComputed } from "zustand-computed" type Store = { count: number label: string inc: () => void } type ComputedStore = { isEven: boolean doubled: number } const computed = createComputed( (state: Store): ComputedStore => ({ isEven: state.count % 2 === 0, doubled: state.count * 2, }), { // Only recompute if the count value itself actually changed shouldRecompute: (prev, next) => "count" in next && prev.count !== (next as Store).count, } ) const useStore = create()( computed((set) => ({ count: 2, label: "hello", inc: () => set((s) => ({ count: s.count + 1 })), })) ) useStore.setState({ label: "world" }) // compute was skipped — `count` was not in the update console.log(useStore.getState().isEven) // true (unchanged, no recompute) useStore.getState().inc() // compute ran — count changed from 2 → 3 console.log(useStore.getState().isEven) // false console.log(useStore.getState().doubled) // 6 ``` -------------------------------- ### createComputed(compute, opts?) Source: https://context7.com/chrisvander/zustand-computed/llms.txt Creates a computed middleware instance that wraps a Zustand StateCreator. It accepts a pure compute function and optional configuration to extend the store's type with computed properties. The compute function is executed on store creation and after relevant state changes. ```APIDOC ## createComputed(compute, opts?) ### Description Creates a computed middleware instance that wraps a Zustand StateCreator. It accepts a pure compute function and optional configuration to extend the store's type with computed properties. The compute function is executed on store creation and after relevant state changes. ### Parameters #### compute - **Function**: A pure function that receives the current state and returns an object of computed properties. #### opts (Optional) - **Object**: Configuration options for the middleware. - **keys** (Array): An array of state keys. If provided, the compute function will only re-run when one of these keys changes. ### Usage Example ```typescript import { create } from "zustand" import { createComputed } from "zustand-computed" type Store = { count: number inc: () => void dec: () => void square: () => void } type ComputedStore = { countSq: number countSqrt: number } // 1. Create the middleware by providing a typed compute function const computed = createComputed((state: Store): ComputedStore => ({ countSq: state.count ** 2, countSqrt: Math.floor(Math.sqrt(state.count)), })) // 2. Wrap your StateCreator with the computed middleware const useStore = create()( computed((set, get) => ({ count: 4, inc: () => set((s) => ({ count: s.count + 1 })), dec: () => set((s) => ({ count: s.count - 1 })), // get() exposes computed fields too square: () => set(() => ({ count: get().countSq })) })) ) // Usage const state = useStore.getState() console.log(state.count) // 4 console.log(state.countSq) // 16 console.log(state.countSqrt) // 2 state.inc() console.log(useStore.getState().count) // 5 console.log(useStore.getState().countSq) // 25 ``` ``` -------------------------------- ### Create and Use Computed State Middleware Source: https://context7.com/chrisvander/zustand-computed/llms.txt Demonstrates how to create a computed middleware instance and wrap a Zustand `StateCreator`. The compute function is called initially and on relevant state changes. Computed fields are accessible via `getState()`. ```typescript import { create } from "zustand" import { createComputed } from "zustand-computed" type Store = { count: number inc: () => void dec: () => void square: () => void } type ComputedStore = { countSq: number countSqrt: number } // 1. Create the middleware by providing a typed compute function const computed = createComputed((state: Store): ComputedStore => ({ countSq: state.count ** 2, countSqrt: Math.floor(Math.sqrt(state.count)), })) // 2. Wrap your StateCreator with the computed middleware const useStore = create()( computed((set, get) => ({ count: 4, inc: () => set((s) => ({ count: s.count + 1 })), dec: () => set((s) => ({ count: s.count - 1 })), // get() exposes computed fields too square: () => set(() => ({ count: get().countSq })), })) ) // Usage const state = useStore.getState() console.log(state.count) // 4 console.log(state.countSq) // 16 console.log(state.countSqrt) // 2 state.inc() console.log(useStore.getState().count) // 5 console.log(useStore.getState().countSq) // 25 ``` -------------------------------- ### Limit Recomputation with `keys` Option Source: https://context7.com/chrisvander/zustand-computed/llms.txt Shows how to use the `keys` option in `createComputed` to specify which state keys should trigger recomputation. This optimizes performance by skipping computation when only non-specified keys change. ```typescript import { create } from "zustand" import { createComputed } from "zustand-computed" type Store = { count: number x: number y: number inc: () => void } type ComputedStore = { countSq: number } // Only recompute when `count` changes; changes to `x` or `y` skip computation const computed = createComputed( (state: Store): ComputedStore => ({ countSq: state.count ** 2 }), { keys: ["count"] } ) const useStore = create( computed((set) => ({ count: 3, x: 0, y: 0, inc: () => set((s) => ({ count: s.count + 1 })), })) ) useStore.setState({ x: 99 }) // compute function was NOT called — countSq remains 9 console.log(useStore.getState().countSq) // 9 useStore.getState().inc() // compute function WAS called — count changed console.log(useStore.getState().countSq) // 16 ``` -------------------------------- ### ComputedStateOpts.keys Source: https://context7.com/chrisvander/zustand-computed/llms.txt An option for `createComputed` that allows you to specify which state keys should trigger a recomputation of the derived state. This can optimize performance by preventing unnecessary computations when unrelated state changes. ```APIDOC ## ComputedStateOpts.keys ### Description Pass a `keys` array to `createComputed` to tell the middleware to only re-run the compute function when one of the listed store keys changes. All other `setState` calls are allowed through but skip the compute step, improving performance in stores with many unrelated fields. ### Usage Example ```typescript import { create } from "zustand" import { createComputed } from "zustand-computed" type Store = { count: number x: number y: number inc: () => void } type ComputedStore = { countSq: number } // Only recompute when `count` changes; changes to `x` or `y` skip computation const computed = createComputed( (state: Store): ComputedStore => ({ countSq: state.count ** 2 }), { keys: ["count"] } ) const useStore = create( computed((set) => ({ count: 3, x: 0, y: 0, inc: () => set((s) => ({ count: s.count + 1 })) })) ) useStore.setState({ x: 99 }) // compute function was NOT called — countSq remains 9 console.log(useStore.getState().countSq) // 9 useStore.getState().inc() // compute function WAS called — count changed console.log(useStore.getState().countSq) // 16 ``` ``` -------------------------------- ### Usage with TypeScript Source: https://github.com/chrisvander/zustand-computed/blob/main/README.md Define types for your store and computed properties when using TypeScript. This ensures type safety and provides better autocompletion. ```typescript import { createComputed } from "zustand-computed" type Store = { count: number inc: () => void dec: () => void } type ComputedStore = { countSq: number } const computed = createComputed((state: Store): ComputedStore => ({ countSq: state.count ** 2, })) const useStore = create()( computed( (set) => ({ count: 1, inc: () => set((state) => ({ count: state.count + 1 })), dec: () => set((state) => ({ count: state.count - 1 })), // get() function has access to ComputedStore square: () => set(() => ({ count: get().countSq })), root: () => set((state) => ({ count: Math.floor(Math.sqrt(state.count)) })), }) ) ) ``` -------------------------------- ### Skipping Computation with `keys` Option Source: https://github.com/chrisvander/zustand-computed/blob/main/README.md Optimize computation by providing a `keys` array to `createComputed`. The computed state will only recompute when the specified state keys change. ```typescript // only recomputes when "count" changes const computed = createComputed((state: Store) => { /* ... */ }, { keys: ["count"] }) // only recomputes when the current state's count does not equal the next state's count (same as above, but more explicit) const computedWithShouldRecomputeFn = createComputed((state: Store) => { /* ... */ }, { shouldRecompute: (state, nextState) => { return state.count !== nextState.count } }) const useStore = create( computed( (set) => ({ count: 1, inc: () => set((state) => ({ count: state.count + 1 })), dec: () => set((state) => ({ count: state.count - 1 })), }) ) ) ``` -------------------------------- ### Using Immer Middleware with Zustand Computed Source: https://context7.com/chrisvander/zustand-computed/llms.txt Demonstrates how zustand-computed correctly handles Immer producers. When Immer mutates a draft and returns undefined, the middleware merges the computed state into the existing draft. ```typescript import { create } from "zustand" import { immer } from "zustand/middleware/immer" import { devtools } from "zustand/middleware" import { createComputed } from "zustand-computed" type Store = { count: number todos: string[] inc: () => void addTodo: (text: string) => void } type ComputedStore = { countSq: number todoCount: number } const computed = createComputed( (state: Store): ComputedStore => ({ countSq: state.count ** 2, todoCount: state.todos.length, }), { keys: ["count", "todos"] } ) const useStore = create()( devtools( immer( computed((set) => ({ count: 3, todos: [], inc: () => set((state) => { // Immer-style mutation — returns undefined state.count += 1 }), addTodo: (text) => set((state) => { state.todos.push(text) }), })) ) ) ) useStore.getState().inc() console.log(useStore.getState().count) // 4 console.log(useStore.getState().countSq) // 16 useStore.getState().addTodo("Buy milk") console.log(useStore.getState().todoCount) // 1 ``` -------------------------------- ### Using the Store in a React Component Source: https://github.com/chrisvander/zustand-computed/blob/main/README.md Access the state and computed properties from the store within a React component using the `useStore` hook. You can then use these values and trigger actions. ```tsx function Counter() { const { count, countSq, inc, dec } = useStore() return (
{count}
{countSq}
) } ``` -------------------------------- ### Applying `createComputed` to a Single Slice Source: https://context7.com/chrisvander/zustand-computed/llms.txt Integrate `createComputed` with Zustand's slices pattern by applying it to a specific slice's `StateCreator`. This ensures the compute function only receives and reacts to that slice's state, preventing unrelated slices from triggering recomputation. ```typescript import { create, type StateCreator } from "zustand" import { createComputed } from "zustand-computed" // --- Types --- type CountSlice = { count: number; dec: () => void } type XYSlice = { x: number; y: number; inc: () => void } type ComputedSlice = { countSq: number } type Store = CountSlice & XYSlice & ComputedSlice // --- Computed targets only the count slice --- const computed = createComputed((state: CountSlice): ComputedSlice => ({ countSq: state.count ** 2, })) const createCountSlice: StateCreator< Store, [], [[ ``` ```typescript chrisvander/zustand-computed", ComputedSlice]], CountSlice & ComputedSlice > = computed((set) => ({ count: 2, dec: () => set((s) => ({ count: s.count - 1 })), })) const createXYSlice: StateCreator = (set) => ({ x: 0, y: 0, // Changing count here does NOT come from the count slice, so // compute does NOT fire when inc() is called from XY slice perspective inc: () => set((s) => ({ count: s.count + 2 })), }) const useStore = create()((...a) => ({ ...createCountSlice(...a), ...createXYSlice(...a), })) console.log(useStore.getState().count) // 2 console.log(useStore.getState().countSq) // 4 useStore.getState().dec() console.log(useStore.getState().count) // 1 console.log(useStore.getState().countSq) // 1 ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.