Skip to main content

zustand-state

Zustand state management patterns including stores, slices, selectors, and middleware. Use when creating or modifying zustand stores, optimizing re-renders, integrating socket events with state, or testing stores.

跳到安装

来源信息

仓库
recca0120/code-quest
最近来源活动
2026年4月19日 22:05
检测到的 SKILL.md 语言
英语
星标
11
分支
2

安装方式

默认使用会先检查来源的 Prompt;你也可以切换为直接命令,或下载本地副本。

检查来源文件

决定是否安装前,请先阅读 SKILL.md,以及 SkillsMP 当前展示的配套文件。

正在显示 SKILL.md

SKILL.md
来源说明 · 只读预览
name
zustand-state
description
Zustand state management patterns including stores, slices, selectors, and middleware. Use when creating or modifying zustand stores, optimizing re-renders, integrating socket events with state, or testing stores.
# Zustand State Management ## Store Creation Prefer `create` for React-bound stores. Use `createStore` (vanilla) only when sharing state outside React (e.g., initializing from a socket listener module). ```ts // stores/useCounterStore.ts import { create } from 'zustand'; import { devtools, immer } from 'zustand/middleware'; interface CounterState { count: number; increment: () => void; } export const useCounterStore = create<CounterState>()( devtools( immer((set) => ({ count: 0, increment: () => set((state) => { state.count += 1; }), })), { name: 'CounterStore' } ) ); ``` ## Slice Pattern (Large Stores) Split domains into slices; apply middleware only at the root. ```ts // stores/slices/chatSlice.ts import { StateCreator } from 'zustand'; import { RootStore } from '../useRootStore'; export interface ChatSlice { messages: string[]; addMessage: (msg: string) => void; } export const createChatSlice: StateCreator<RootStore, [['zustand/immer', never]], [], ChatSlice> = (set) => ({ messages: [], addMessage: (msg) => set((state) => { state.messages.push(msg); }), }); // stores/useRootStore.ts export type RootStore = ChatSlice & OtherSlice; export const useRootStore = create<RootStore>()( devtools(immer((...a) => ({ ...createChatSlice(...a), ...createOtherSlice(...a) }))) ); ``` ## Selectors and Re-render Optimization Subscribe to the minimal shape needed. Use `useShallow` for object/array selections; use atomic selectors for primitives. ```ts // Primitive — no useShallow needed const count = useCounterStore((s) => s.count); // Multiple fields — use useShallow to avoid identity churn import { useShallow } from 'zustand/react/shallow'; const { name, role } = useUserStore(useShallow((s) => ({ name: s.name, role: s.role }))); // Stable action reference — select once outside render or memoize const addMessage = useRootStore((s) => s.addMessage); ``` ## Middleware Stack Order ``` devtools( persist( subscribeWithSelector( immer( ... ) ) ) ) ``` - `devtools` outermost for full time-travel visibility - `persist` before `subscribeWithSelector` to hydrate before subscriptions fire - `immer` innermost so mutating syntax works inside all middleware ## Async Actions Keep async logic inside the store action; no thunks or sagas needed. ```ts fetchUser: async (id) => { set({ loading: true }); try { const user = await api.getUser(id); set({ user, loading: false }); } catch (err) { set({ error: String(err), loading: false }); } }, ``` ## Socket.io Real-time Integration Initialize subscriptions outside React; push to store via actions. ```ts // lib/socketSync.ts import { socket } from './socket'; import { useRootStore } from '../stores/useRootStore'; socket.on('message', (msg) => useRootStore.getState().addMessage(msg)); socket.on('connect', () => useRootStore.setState({ connected: true })); ``` Use `subscribeWithSelector` middleware when a non-React module needs to react to store changes. ```ts useRootStore.subscribe( (s) => s.connected, (connected) => { if (connected) socket.emit('ready'); } ); ``` ## Testing Stores Test store logic directly without React; reset state between tests. ```ts import { useCounterStore } from '../stores/useCounterStore'; beforeEach(() => useCounterStore.setState({ count: 0 })); it('increments', () => { useCounterStore.getState().increment(); expect(useCounterStore.getState().count).toBe(1); }); ``` ## Common Pitfalls | Pitfall | Fix | |---|---| | Selecting entire store object | Use individual or `useShallow` selectors | | Inline selector `(s) => ({ a: s.a })` without `useShallow` | Wrap with `useShallow` | | Calling async socket setup inside component | Move to module-level init | | Applying middleware inside a slice | Apply only at root `create()` call | | Forgetting `devtools` name option | Always set `{ name: 'StoreName' }` for DevTools clarity | Sources: - [Working with Zustand – TkDodo](https://tkdodo.eu/blog/working-with-zustand) - [Zustand Best Practices – projectrules.ai](https://www.projectrules.ai/rules/zustand) - [Slices Pattern – DeepWiki](https://deepwiki.com/pmndrs/zustand/7.1-slices-pattern) - [Immer Middleware – DeepWiki](https://deepwiki.com/pmndrs/zustand/3.6-immer-middleware) - [useShallow vs selectors – GitHub Discussion](https://github.com/pmndrs/zustand/discussions/2541) - [Slice-Based Store for Next.js 14 + TypeScript – Atlys Engineering](https://engineering.atlys.com/a-slice-based-zustand-store-for-next-js-14-and-typescript-6b92385a48f5)
在 GitHub 查看