mcpField
Z
zustand-state-management

zustand-state-management

github.com/mindrally开发289973 次安装3 周前更新
代码仓库

关于 zustand-state-management

面向 React 和 TypeScript 应用的 Zustand 状态管理指导,聚焦共享客户端状态、类型化 Store、选择器性能、中间件、SSR 与测试。

主要功能

  1. 01状态归属决策指导
  2. 02小型 Store 与类型化切片
  3. 03选择器性能实践
  4. 04持久化与 DevTools 中间件
  5. 05SSR 安全的 Store 与测试

适用场景

  1. 01设计 React 组件间的共享状态
  2. 02通过精简选择器减少重复渲染
  3. 03测试 Store 操作与中间件
  4. 04处理 SSR 应用中的持久化状态

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/mindrally/skills --skill zustand-state-management

安装后在 Claude Code 中输入/zustand-state-management确认已加载。

详细介绍

Zustand 状态管理

此技能介绍如何在 React 和 TypeScript 应用中设计和使用 Zustand store,包括状态归属决策、store 切片、选择器性能、中间件、SSR 和测试。

状态归属

  • 将短暂的 UI 状态保留在最近的组件中,并使用 useState 或 useReducer 管理——不要默认使用 Zustand。
  • 使用 URL 状态保存可分享的筛选条件、分页、标签页和搜索参数。
  • 仅在客户端状态确实需要由互不相关的组件共享时才使用 Zustand(例如身份验证会话视图状态、命令面板、购物车草稿、编辑器状态等)。
  • 使用 TanStack Query、SWR、RTK Query 或项目现有的数据层管理服务端状态——除非有明确记录的离线或草稿编辑需求,否则不要将获取的服务端数据复制到 Zustand store 中。
  • 使用函数式、声明式模式;避免使用类。使用 isLoading、hasError 这类带辅助动词的描述性变量名。

Store 设计

  • 将每个 store 建模为状态加命名操作;避免暴露供组件代码滥用的匿名 setter。
  • 保持 store 小巧且专注于特定领域,而不是为整个应用创建一个全局 store。
  • 将大型 store 拆分为有类型的切片,然后仅在组合后的 store 边界应用中间件。
  • 将派生值保留为选择器或小型纯辅助函数,除非必须将其缓存到状态中。
  • 默认存储可序列化数据;将 DOM 节点、promise、套接字和计时器置于 store 状态之外。
import { create } from 'zustand'

interface SidebarState {
  isOpen: boolean
  activePanelId: string | null
}

interface SidebarActions {
  openPanel: (panelId: string) => void
  close: () => void
  toggle: () => void
}

type SidebarStore = SidebarState & SidebarActions

export const useSidebarStore = create<SidebarStore>()((set) => ({
  isOpen: false,
  activePanelId: null,
  openPanel: (panelId) => set({ isOpen: true, activePanelId: panelId }),
  close: () => set({ isOpen: false, activePanelId: null }),
  toggle: () => set((state) => ({ isOpen: !state.isOpen })),
}))

组件用法与性能

  • 订阅尽可能小的切片:useStore((state) => state.value)。避免选择整个 store。
  • 除非组件确实需要每个字段,否则不要在组件中调用不带选择器的 store hook。
  • 当选择器返回包含多个值的对象或元组时,使用 zustand/react/shallow 中的 useShallow,以避免每次调用产生新的对象引用而触发重新渲染。
  • 保持选择器纯净且开销小;将昂贵的派生计算移至带记忆化的辅助函数中。
import { useShallow } from 'zustand/react/shallow'
import { useSidebarStore } from '@/stores/sidebar-store'

export function SidebarToggle() {
  const { isOpen, toggle } = useSidebarStore(
    useShallow((state) => ({
      isOpen: state.isOpen,
      toggle: state.toggle,
    })),
  )

  return (
    <button type="button" aria-expanded={isOpen} onClick={toggle}>
      Toggle sidebar
    </button>
  )
}

TypeScript

  • 为共享 store 定义显式的状态和操作接口;对于非简单 store,将它们组合起来(type Store = State & Actions),而不是使用一个扁平接口。
  • 避免使用 any;处理外部数据时使用 unknown 并进行类型收窄。
  • 为操作的参数和返回值声明类型,包括异步操作。
  • 对于复杂的本地状态,优先使用判别联合类型,而不是多个关联松散的布尔值。
  • 当测试、实用工具或 vanilla store 工厂需要时,导出 store 状态类型。

中间件

更新
  • 当下一个值取决于当前状态时,使用函数式 set((state) => nextState)。
  • 以不可变方式处理嵌套状态;仅当 immer 中间件能显著简化嵌套更新时才安装并使用它——不使用该中间件时,不要直接修改嵌套对象。
持久化
import { persist } from 'zustand/middleware'

const useStore = create(
  persist(
    (set) => ({
      // state and actions
    }),
    { name: 'store-key' }
  )
)
  • 仅对必须在重新加载后保留的状态使用 persist。
  • 持久化超出简单偏好设置的内容时,使用 partialize、version 和 migrate。
  • 切勿将密钥、访问令牌、刷新令牌、原始个人身份信息或其他长期授权状态持久化到浏览器存储中。
DevTools
import { devtools } from 'zustand/middleware'

const useStore = create(
  devtools((set) => ({
    // state and actions
  }))
)
  • 在开发环境中,对复杂流程使用 devtools,并为重要操作设置清晰的名称,以便在跟踪记录中识别。
非 React 订阅
  • 对于需要在组件渲染之外进行细粒度更新的非 React 订阅,使用 subscribeWithSelector。

异步操作与错误处理

  • 异步 store 操作应协调仅限客户端的工作流、乐观草稿或本地设备 API;除非状态明确归客户端所有,否则应将 HTTP 获取放在项目的服务端状态层。
  • 使用 idle、pending、success 和 error 等明确状态表示异步客户端工作流,而不是使用临时布尔值。
  • 在函数开头通过提前返回和守卫子句处理错误;在异步操作中使用 try-catch,并提供有意义的错误消息。
  • 重试或关闭工作流时,有意重置错误状态。

SSR 和 React Server Components

  • 不要在 React Server Components 中读取或修改仅限浏览器的 store。
  • 在 SSR 框架(Next.js App Router 等)中,如果必须在服务器上初始化状态,应为每个请求创建 vanilla store,而不是使用模块级共享 store。
  • 在渲染由存储支持的值之前,防止持久化 store 导致 hydration 不匹配。
  • 除中间件配置外,store 模块中不要直接访问 window、document 和存储。

测试

  • 独立于组件测试 store;尽可能直接测试 store 操作,而不渲染 React。
  • 使用初始状态在各项测试之间重置 store。
  • 将选择器和操作的断言与组件行为分开;隔离 UI 逻辑时,在组件测试中模拟 Zustand store。
  • 模拟服务端状态库,而不是为了测试将获取的数据经由 Zustand 传递。
  • 将中间件行为(持久化、devtools)与核心 store 逻辑分开测试。

反模式

  • 不要为整个应用创建一个全局 store。
  • 除非多个相距较远的组件需要编辑同一份草稿,否则不要将表单输入状态放入 Zustand。
  • 不要在未使用 Immer 中间件的情况下直接修改嵌套对象。
  • 不要将 Zustand 用作事件总线;优先使用显式回调、服务或作用域限定的 store。
  • 除非项目已经采用这种模式,否则不要引入 Redux 风格的 reducer、action 常量或 dispatch 封装。

评论

支持 Markdown