Zustand Computed State Middleware
Zustand Computed State is a lightweight, TypeScript-friendly middleware designed for the Zustand state management library. It allows developers to define derived or 'computed' state values that automatically update whenever their base dependencies in the Zustand store change. This eliminates the need for manual re-computation of derived values, simplifying state logic and improving performance by only re-calculating when necessary. The current stable version is 0.2.0, indicating it's in an early but active development phase, with releases likely occurring as features stabilize. Its key differentiator is its straightforward integration into Zustand's middleware chain, providing a clear pattern for computed properties without introducing significant overhead or complex APIs, making it a good choice for applications already leveraging Zustand that require derived state.
Common errors
-
TypeError: create is not a function
cause Attempting to use `create` without importing it from `zustand`.fixEnsure `import { create } from 'zustand';` is present at the top of your file. -
Property 'computedProperty' does not exist on type 'StoreType'
cause The TypeScript type for your store (`StoreType`) has not been updated to include the newly defined computed properties.fixManually add the computed properties and their types to your `StoreType` interface or type alias. For example, `type Store = { count: number; countSq: number; };`
Warnings
- gotcha When using `compute` with multiple slices, ensure you provide a unique identifier string as the first argument to `compute` to correctly separate computed states for each slice. Failure to do so might lead to computed state conflicts or incorrect updates between slices.
- gotcha The `get()` function *inside* the `computed` middleware's state creator has access to the *currently evaluated* computed states. This means computed properties can depend on other computed properties defined within the same `compute` block, but careful structuring is needed for dependencies across different `compute` calls or outside the middleware.
- breaking The package currently targets `zustand` v5.0.8 via peer dependencies. Future major versions of `zustand` might introduce breaking changes that could affect the compatibility of this middleware, requiring an update to `zustand-computed-state`.
Install
-
npm install zustand-computed-state -
yarn add zustand-computed-state -
pnpm add zustand-computed-state
Imports
- computed
const { computed } = require('zustand-computed-state');import { computed } from 'zustand-computed-state'; - compute
import compute from 'zustand-computed-state';
import { compute } from 'zustand-computed-state'; - StateCreator
import { StateCreator } from 'zustand';
Quickstart
import { create } from 'zustand';
import { computed, compute } from 'zustand-computed-state';
type Store = {
count: number;
inc: () => void;
dec: () => void;
countSq: number; // Computed state
};
const useStore = create<Store>()(
computed((set, get) => ({
count: 1,
inc: () => set(state => ({ count: state.count + 1 })),
dec: () => set(state => ({ count: state.count - 1 })),
// Example: get() can access computed states
square: () => set(() => ({ count: get().countSq })),
root: () => set(state => ({ count: Math.floor(Math.sqrt(state.count)) })),
// Define computed state 'countSq' based on 'count'
...compute(get, state => ({
countSq: state.count ** 2,
})),
}))
);
// Usage in a component (example, not runnable here)
/*
function Counter() {
const { count, countSq, inc, dec } = useStore();
return (
<div>
<span>Count: {count}</span><br />
<span>Count Squared: {countSq}</span><br />
<button onClick={inc}>+1</button>
<button onClick={dec}>-1</button>
</div>
);
}
*/
console.log('Initial state:', useStore.getState());
useStore.getState().inc();
console.log('After increment:', useStore.getState());