mcpField
Z
zustand-state-management

zustand-state-management

github.com/fixmyberlin开发059 次安装1 天前更新
代码仓库

关于 zustand-state-management

为 React 应用提供 Zustand v5 使用规范的开发技能,重点涵盖小型 Store、自定义 Hook、选择器模式,以及区分客户端 UI 状态与服务端和 URL 状态。

主要功能

  1. 01Zustand v5 Store 规范
  2. 02自定义 Hook 导出模式
  3. 03原子选择器指导
  4. 04事件式 Action 设计
  5. 05服务端与 URL 状态边界

适用场景

  1. 01在组件间共享 UI 状态
  2. 02保存临时客户端偏好
  3. 03通过自定义 Hook 结合 TanStack Query
  4. 04按功能组织 Store

安装

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

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

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

详细介绍

**大模型参考:**获取 llms.txt,查看文档索引和最新 API。完整文档:llms-full.txt。面向读者的文档:zustand.docs.pmnd.rs。

核心思路:使用 Zustand(Dominik Dorfmeister)——仅导出自定义钩子,使用原子选择器、actions 命名空间、事件式操作,拆分为多个小型 store,并通过自定义钩子与 Query/URL 结合。

**版本:**Zustand v5。在应用中固定使用 zustand@^5;升级时请查看 迁移到 v5。


何时使用 Zustand

使用 Zustand 的场景使用其他方案的场景
在多个组件间共享的客户端 UI 状态(面板、地图工具、向导步骤)服务端数据 → TanStack Query
不在 URL 中的临时客户端偏好设置可共享的 URL 状态 → 路由器搜索参数(不使用 Router 的 Vite 应用:nuqs);切勿在 Zustand 中重复存储
小型、同步的客户端状态身份验证会话 → skill tanstack-start-auth

大多数应用状态都属于服务端状态或 URL 状态。只有在需要尚未由其他方案处理的客户端全局状态时,才添加 Zustand store。


文件和导出命名

**一个 store 文件对应一个关注点。**与功能放在一起(Start 项目的目录结构请参见 tanstack-start-conventions;SPA 应用:放在 components/ / shared/ 下的功能文件夹中)。

项目约定示例
文件{domain}-store.ts(kebab-case,以 -store 结尾)map-filters-store.ts
文件夹放在功能旁,例如 src/features/map/除非内容共享,否则不要将所有内容堆在一个全局 stores/ 文件夹中
状态类型{Domain}Store(PascalCase 接口)MapFiltersStore
内部 create 钩子use{Domain}Store——绝不导出useMapFiltersStore
导出的钩子状态基本项使用 use{Slice};actions 使用 use{Domain}ActionsuseAppliedFilters、useMapFiltersActions

不要使用:store.ts、state.ts、zustand.ts 或 useMapFiltersStore.ts(文件名不得暗示会导出原始 store)。不要在一个文件中放入互不相关的领域。

**持久化:**每个 store 使用唯一的 name,并与文件名对应,例如,map-filters-store.ts 对应 fmc-map-filters。**TanStack Start SSR + persist:**遵循 skill tanstack-start-conventions 和 llms.txt → SSR and Hydration(水合门控或官方 SSR 安全模式)。


标准 store 结构

import { create } from 'zustand'

interface MapFiltersStore {
  applied: string[]
  actions: {
    addFilter: (filter: string) => void
    clearFilters: () => void
  }
}

const useMapFiltersStore = create<MapFiltersStore>()((set) => ({
  applied: [],
  actions: {
    addFilter: (filter) =>
      set((state) => (state.applied.includes(filter) ? state : { applied: [...state.applied, filter] })),
    clearFilters: () => set({ applied: [] }),
  },
}))

export const useAppliedFilters = () => useMapFiltersStore((state) => state.applied)

export const useMapFiltersActions = () => useMapFiltersStore((state) => state.actions)

**TypeScript:**始终使用 create<StoreType>()((set) => …)(双括号),即使不使用中间件也一样。


规则

  1. 仅导出自定义 Hook(use{Slice} + use{Domain}Actions);将 use{Domain}Store 保持为私有。调用 store Hook 时必须传入选择器。
  2. 使用原子选择器,返回基本类型或稳定引用;每个 slice 导出一个 Hook。只有在两个值很少需要同时使用时,才使用 useShallow(来自 zustand/shallow),条件允许时优先使用原子 Hook。
  3. 将 actions 与状态分开。 actions 引用是稳定的,因此使用 const { addFilter } = useMapFiltersActions() 没问题。
  4. **将操作建模为事件,而非设置器:**使用 addFilter、closePanel,而非在组件中调用 setApplied。业务逻辑留在 store 中。对于高频输入(调整大小、观察器),如果没有变化,则从 set 返回之前的 state。
  5. 优先使用多个小型 store,而非一个全应用级 store。通过自定义 Hook 在边界处组合(例如,读取 useCredentialsStore + useUsersStore 的 Hook)。除非有充分理由,否则避免使用 slices 模式(TypeScript 复杂度较高;参见 llms.txt)。
  6. **通过自定义 Hook 与 TanStack Query 组合使用:**筛选条件放在 Zustand 中,服务端列表放在 Query 中(queryKey: ['todos', filters])。
  7. 不要用 Zustand 存储从服务端获取的数据。

查阅 llms.txt

不要在此技能中重复完整的 API 文档。请获取 llms.txt,了解中间件(persist、devtools、immer;persist 从 zustand/middleware 导入)、模式(slices、重置状态、vanilla store、测试)、集成(SSR 和 Hydration、URL hash、Maps/Sets)以及 v4 → v5 迁移。


相关技能

主题技能
应用布局、SSR(Start)tanstack-start-conventions
URL 搜索(Router)tanstack-router-conventions
URL 查询状态(无 Router)nuqs

评论

支持 Markdown