Zustand状态管理技能Skill zustand-state-management

这个技能提供了在 React 应用中,使用 Zustand 进行状态管理的完整设置指南,特别针对 TypeScript。它包括基本模式、中间件配置、常见问题预防和最佳实践,用于构建可扩展、类型安全的全局状态。关键词:Zustand,状态管理,React,TypeScript,持久化,中间件,前端开发,JavaScript,Redux 替代。

前端开发 0 次安装 0 次浏览 更新于 3/20/2026

名称:zustand-state-management 描述:| 用于 React 应用的 Zustand 状态管理生产测试设置。 此技能提供了构建可扩展、类型安全的全局状态的全面模式。

使用场景:在 React 中设置全局状态,从 Redux 或 Context API 迁移,使用 localStorage 实现状态持久化,配置 TypeScript 与 Zustand,使用切片模式实现模块化存储,添加 devtools 中间件进行调试,处理 Next.js SSR 水合,或遇到水合错误、TypeScript 推断问题或持久化中间件问题。

预防 5 个文档化问题:Next.js 水合不匹配、TypeScript 双括号语法错误、持久化中间件导出错误、无限渲染循环和切片模式类型推断失败。

关键词:zustand,状态管理,React 状态,TypeScript 状态,持久化中间件,devtools,切片模式,全局状态,React hooks,create store,useBoundStore,StateCreator,水合错误,文本内容不匹配,无限渲染,localStorage,sessionStorage,immer 中间件,浅相等,选择器模式,zustand v5 许可证:MIT

Zustand 状态管理

状态:生产就绪 ✅ 最后更新:2025-10-24 最新版本:zustand@5.0.8 依赖项:React 18+,TypeScript 5+


快速开始(3分钟)

1. 安装 Zustand

npm install zustand
# 或
pnpm add zustand
# 或
yarn add zustand

为什么选择 Zustand?

  • 最小 API:只需学习 1 个函数(create
  • 无模板代码:无需提供者、reducer 或动作
  • TypeScript 优先:优秀的类型推断
  • 快速:细粒度订阅防止不必要的重渲染
  • 灵活:用于持久化、devtools 等的中间件

2. 创建您的第一个存储(TypeScript)

import { create } from 'zustand'

interface BearStore {
  bears: number
  increase: (by: number) => void
  reset: () => void
}

const useBearStore = create<BearStore>()((set) => ({
  bears: 0,
  increase: (by) => set((state) => ({ bears: state.bears + by })),
  reset: () => set({ bears: 0 }),
}))

关键:注意 双括号 create<T>()() - 对于 TypeScript 与中间件,这是必需的。

3. 在组件中使用存储

import { useBearStore } from './store'

function BearCounter() {
  const bears = useBearStore((state) => state.bears)
  return <h1>{bears} around here...</h1>
}

function Controls() {
  const increase = useBearStore((state) => state.increase)
  return <button onClick={() => increase(1)}>Add bear</button>
}

为什么有效:

  • 组件仅在选择的状态更改时重渲染
  • 无需 Context 提供者
  • 选择器函数提取特定状态切片

3 模式设置过程

模式 1:基本存储(JavaScript)

对于没有 TypeScript 的简单用例:

import { create } from 'zustand'

const useStore = create((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
  decrement: () => set((state) => ({ count: state.count - 1 })),
}))

使用场景:

  • 原型设计
  • 小型应用
  • 项目中没有 TypeScript

模式 2:TypeScript 存储(推荐)

对于需要类型安全的生产应用:

import { create } from 'zustand'

// 定义存储接口
interface CounterStore {
  count: number
  increment: () => void
  decrement: () => void
}

// 创建类型化存储
const useCounterStore = create<CounterStore>()((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
  decrement: () => set((state) => ({ count: state.count - 1 })),
}))

关键点:

  • 为状态和动作定义独立接口
  • 使用 create<T>()() 语法(柯里化以兼容中间件)
  • 完整的 IDE 自动补全和类型检查

模式 3:持久化存储

用于状态在页面重载后保留:

import { create } from 'zustand'
import { persist, createJSONStorage } from 'zustand/middleware'

interface UserPreferences {
  theme: 'light' | 'dark' | 'system'
  language: string
  setTheme: (theme: UserPreferences['theme']) => void
  setLanguage: (language: string) => void
}

const usePreferencesStore = create<UserPreferences>()(
  persist(
    (set) => ({
      theme: 'system',
      language: 'en',
      setTheme: (theme) => set({ theme }),
      setLanguage: (language) => set({ language }),
    }),
    {
      name: 'user-preferences', // localStorage 中的唯一名称
      storage: createJSONStorage(() => localStorage), // 可选:默认为 localStorage
    },
  ),
)

重要性:

  • 状态自动保存到 localStorage
  • 在页面重载时恢复
  • 也适用于 sessionStorage
  • 自动处理序列化

关键规则

始终执行

✅ 在 TypeScript 中使用 create<T>()()(双括号)以兼容中间件 ✅ 为状态和动作定义独立接口 ✅ 使用选择器函数提取特定状态切片 ✅ 使用 set 与更新器函数用于派生状态:set((state) => ({ count: state.count + 1 })) ✅ 为持久化中间件存储键使用唯一名称 ✅ 使用 hasHydrated 标志模式处理 Next.js 水合 ✅ 使用 shallow 选择多个值 ✅ 保持动作纯正(除了状态更新外无副作用)

永不执行

❌ 在 TypeScript 中使用 create<T>(...)(单括号) - 破坏中间件类型 ❌ 直接突变状态:set((state) => { state.count++; return state }) - 使用不可变更新 ❌ 在选择器中创建新对象:useStore((state) => ({ a: state.a })) - 导致无限渲染 ❌ 为多个存储使用相同存储名称 - 导致数据冲突 ❌ 在 SSR 中未检查水合情况下访问 localStorage ❌ 使用 Zustand 处理服务器状态 - 使用 TanStack Query 替代 ❌ 直接导出存储实例 - 始终导出钩子


已知问题预防

此技能预防 5 个文档化问题:

问题 #1:Next.js 水合不匹配

错误"文本内容与服务器渲染的 HTML 不匹配""水合失败"

来源

原因:持久化中间件在客户端读取 localStorage,但在服务器端不读取,导致状态不匹配。

预防

import { create } from 'zustand'
import { persist } from 'zustand/middleware'

interface StoreWithHydration {
  count: number
  _hasHydrated: boolean
  setHasHydrated: (hydrated: boolean) => void
  increase: () => void
}

const useStore = create<StoreWithHydration>()(
  persist(
    (set) => ({
      count: 0,
      _hasHydrated: false,
      setHasHydrated: (hydrated) => set({ _hasHydrated: hydrated }),
      increase: () => set((state) => ({ count: state.count + 1 })),
    }),
    {
      name: 'my-store',
      onRehydrateStorage: () => (state) => {
        state?.setHasHydrated(true)
      },
    },
  ),
)

// 在组件中
function MyComponent() {
  const hasHydrated = useStore((state) => state._hasHydrated)

  if (!hasHydrated) {
    return <div>Loading...</div>
  }

  // 现在可以安全渲染持久化状态
  return <ActualContent />
}

问题 #2:TypeScript 双括号缺失

错误:类型推断失败,StateCreator 类型与中间件中断

来源官方 Zustand TypeScript 指南

原因:柯里化语法 create<T>()() 是中间件与 TypeScript 推断工作所必需的。

预防

// ❌ 错误 - 单括号
const useStore = create<MyStore>((set) => ({
  // ...
}))

// ✅ 正确 - 双括号
const useStore = create<MyStore>()((set) => ({
  // ...
}))

规则:始终在 TypeScript 中使用 create<T>()(),即使没有中间件(为未来兼容)。

问题 #3:持久化中间件导入错误

错误"尝试导入错误:'createJSONStorage' 未从 'zustand/middleware' 导出"

来源:GitHub 讨论 #2839

原因:错误的导入路径或 zustand 与构建工具之间的版本不匹配。

预防

// ✅ v5 的正确导入
import { create } from 'zustand'
import { persist, createJSONStorage } from 'zustand/middleware'

// 验证版本
// zustand@5.0.8 包括 createJSONStorage
// zustand@4.x 使用不同 API

// 检查您的 package.json
// "zustand": "^5.0.8"

问题 #4:无限渲染循环

错误:组件无限重渲染,浏览器冻结

来源:GitHub 讨论 #2642

原因:在选择器中创建新对象引用导致 Zustand 认为状态已更改。

预防

import { shallow } from 'zustand/shallow'

// ❌ 错误 - 每次创建新对象
const { bears, fishes } = useStore((state) => ({
  bears: state.bears,
  fishes: state.fishes,
}))

// ✅ 正确选项 1 - 分别选择原始值
const bears = useStore((state) => state.bears)
const fishes = useStore((state) => state.fishes)

// ✅ 正确选项 2 - 使用 shallow 选择多个值
const { bears, fishes } = useStore(
  (state) => ({ bears: state.bears, fishes: state.fishes }),
  shallow,
)

问题 #5:切片模式 TypeScript 复杂性

错误StateCreator 类型推断失败,复杂中间件类型中断

来源官方切片模式指南

原因:组合多个切片需要显式类型注解以兼容中间件。

预防

import { create, StateCreator } from 'zustand'

// 定义切片类型
interface BearSlice {
  bears: number
  addBear: () => void
}

interface FishSlice {
  fishes: number
  addFish: () => void
}

// 使用正确类型创建切片
const createBearSlice: StateCreator<
  BearSlice & FishSlice,  // 组合存储类型
  [],                      // 中间件修改器(如果没有则为空)
  [],                      // 链式中间件(如果没有则为空)
  BearSlice               // 此切片的类型
> = (set) => ({
  bears: 0,
  addBear: () => set((state) => ({ bears: state.bears + 1 })),
})

const createFishSlice: StateCreator<
  BearSlice & FishSlice,
  [],
  [],
  FishSlice
> = (set) => ({
  fishes: 0,
  addFish: () => set((state) => ({ fishes: state.fishes + 1 })),
})

// 组合切片
const useStore = create<BearSlice & FishSlice>()((...a) => ({
  ...createBearSlice(...a),
  ...createFishSlice(...a),
}))

中间件配置

持久化中间件(localStorage)

import { create } from 'zustand'
import { persist, createJSONStorage } from 'zustand/middleware'

interface MyStore {
  data: string[]
  addItem: (item: string) => void
}

const useStore = create<MyStore>()(
  persist(
    (set) => ({
      data: [],
      addItem: (item) => set((state) => ({ data: [...state.data, item] })),
    }),
    {
      name: 'my-storage',
      storage: createJSONStorage(() => localStorage),
      partialize: (state) => ({ data: state.data }), // 仅持久化 'data'
    },
  ),
)

Devtools 中间件(Redux DevTools)

import { create } from 'zustand'
import { devtools } from 'zustand/middleware'

interface CounterStore {
  count: number
  increment: () => void
}

const useStore = create<CounterStore>()(
  devtools(
    (set) => ({
      count: 0,
      increment: () =>
        set(
          (state) => ({ count: state.count + 1 }),
          undefined,
          'counter/increment', // DevTools 中的动作名称
        ),
    }),
    { name: 'CounterStore' }, // DevTools 中的存储名称
  ),
)

组合多个中间件

import { create } from 'zustand'
import { devtools, persist } from 'zustand/middleware'

const useStore = create<MyStore>()(
  devtools(
    persist(
      (set) => ({
        // 存储定义
      }),
      { name: 'my-storage' },
    ),
    { name: 'MyStore' },
  ),
)

顺序重要devtools(persist(...)) 在 DevTools 中显示持久化动作。


常见模式

模式:计算/派生值

interface StoreWithComputed {
  items: string[]
  addItem: (item: string) => void
  // 在选择器中计算,不存储
}

const useStore = create<StoreWithComputed>()((set) => ({
  items: [],
  addItem: (item) => set((state) => ({ items: [...state.items, item] })),
}))

// 在组件中使用
function ItemCount() {
  const count = useStore((state) => state.items.length)
  return <div>{count} items</div>
}

模式:异步动作

interface AsyncStore {
  data: string | null
  isLoading: boolean
  error: string | null
  fetchData: () => Promise<void>
}

const useAsyncStore = create<AsyncStore>()((set) => ({
  data: null,
  isLoading: false,
  error: null,
  fetchData: async () => {
    set({ isLoading: true, error: null })
    try {
      const response = await fetch('/api/data')
      const data = await response.text()
      set({ data, isLoading: false })
    } catch (error) {
      set({ error: (error as Error).message, isLoading: false })
    }
  },
}))

模式:重置存储

interface ResettableStore {
  count: number
  name: string
  increment: () => void
  reset: () => void
}

const initialState = {
  count: 0,
  name: '',
}

const useStore = create<ResettableStore>()((set) => ({
  ...initialState,
  increment: () => set((state) => ({ count: state.count + 1 })),
  reset: () => set(initialState),
}))

模式:带参数的选择器

interface TodoStore {
  todos: Array<{ id: string; text: string; done: boolean }>
  addTodo: (text: string) => void
  toggleTodo: (id: string) => void
}

const useStore = create<TodoStore>()((set) => ({
  todos: [],
  addTodo: (text) =>
    set((state) => ({
      todos: [...state.todos, { id: Date.now().toString(), text, done: false }],
    })),
  toggleTodo: (id) =>
    set((state) => ({
      todos: state.todos.map((todo) =>
        todo.id === id ? { ...todo, done: !todo.done } : todo
      ),
    })),
}))

// 带参数使用
function Todo({ id }: { id: string }) {
  const todo = useStore((state) => state.todos.find((t) => t.id === id))
  const toggleTodo = useStore((state) => state.toggleTodo)

  if (!todo) return null

  return (
    <div>
      <input
        type="checkbox"
        checked={todo.done}
        onChange={() => toggleTodo(id)}
      />
      {todo.text}
    </div>
  )
}

使用捆绑资源

模板(templates/)

此技能包括 8 个现成可用的模板文件:

  • basic-store.ts - 最小 JavaScript 存储示例
  • typescript-store.ts - 正确类型化的 TypeScript 存储
  • persist-store.ts - 带迁移的 localStorage 持久化
  • slices-pattern.ts - 模块化存储组织
  • devtools-store.ts - Redux DevTools 集成
  • nextjs-store.ts - 支持 SSR 的 Next.js 存储与水合
  • computed-store.ts - 派生状态模式
  • async-actions-store.ts - 带加载状态的异步操作

示例用法:

# 复制模板到您的项目
cp ~/.claude/skills/zustand-state-management/templates/typescript-store.ts src/store/

何时使用每个:

  • 使用 basic-store.ts 用于快速原型
  • 使用 typescript-store.ts 用于大多数生产应用
  • 使用 persist-store.ts 当状态需要在页面重载后保留时
  • 使用 slices-pattern.ts 用于大型、复杂存储(100+ 行)
  • 使用 nextjs-store.ts 用于具有 SSR 的 Next.js 项目

参考(references/)

复杂场景的深度文档:

  • middleware-guide.md - 完整中间件文档(持久化,devtools,immer,自定义)
  • typescript-patterns.md - 高级 TypeScript 模式和故障排除
  • nextjs-hydration.md - SSR、水合和 Next.js 最佳实践
  • migration-guide.md - 从 Redux、Context API 或 Zustand v4 迁移

Claude 何时应加载这些:

  • 当用户询问持久化、devtools 或自定义中间件时,加载 middleware-guide.md
  • 当遇到复杂类型推断问题时,加载 typescript-patterns.md
  • 对于 Next.js 特定问题,加载 nextjs-hydration.md
  • 当从其他状态管理解决方案迁移时,加载 migration-guide.md

脚本(scripts/)

  • check-versions.sh - 验证 Zustand 版本和兼容性

用法:

cd your-project/
~/.claude/skills/zustand-state-management/scripts/check-versions.sh

高级主题

Vanilla 存储(无 React)

import { createStore } from 'zustand/vanilla'

const store = createStore<CounterStore>()((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
}))

// 订阅更改
const unsubscribe = store.subscribe((state) => {
  console.log('Count changed:', state.count)
})

// 获取当前状态
console.log(store.getState().count)

// 更新状态
store.getState().increment()

// 清理
unsubscribe()

自定义中间件

import { StateCreator, StoreMutatorIdentifier } from 'zustand'

type Logger = <T>(
  f: StateCreator<T, [], []>,
  name?: string,
) => StateCreator<T, [], []>

const logger: Logger = (f, name) => (set, get, store) => {
  const loggedSet: typeof set = (...a) => {
    set(...(a as Parameters<typeof set>))
    console.log(`[${name}]:`, get())
  }
  return f(loggedSet, get, store)
}

// 使用自定义中间件
const useStore = create<MyStore>()(
  logger((set) => ({
    // 存储定义
  }), 'MyStore'),
)

Immer 中间件(可变更新)

import { create } from 'zustand'
import { immer } from 'zustand/middleware/immer'

interface TodoStore {
  todos: Array<{ id: string; text: string }>
  addTodo: (text: string) => void
}

const useStore = create<TodoStore>()(
  immer((set) => ({
    todos: [],
    addTodo: (text) =>
      set((state) => {
        // 使用 Immer 直接突变
        state.todos.push({ id: Date.now().toString(), text })
      }),
  })),
)

依赖项

必需

  • zustand@5.0.8 - 状态管理库
  • react@18.0.0+ - React 框架

可选

  • @types/node - 用于 TypeScript 路径解析
  • immer - 用于可变更新语法
  • Redux DevTools 扩展 - 用于 devtools 中间件

官方文档


包版本(验证于 2025-10-24)

{
  "dependencies": {
    "zustand": "^5.0.8",
    "react": "^19.0.0"
  },
  "devDependencies": {
    "@types/node": "^22.0.0",
    "typescript": "^5.0.0"
  }
}

兼容性

  • React 18+,React 19 ✅
  • TypeScript 5+ ✅
  • Next.js 14+,Next.js 15+ ✅
  • Vite 5+ ✅

故障排除

问题:存储更新不触发重渲染

解决方案:确保使用选择器函数,而不是解构:const bears = useStore(state => state.bears) 而非 const { bears } = useStore()

问题:TypeScript 错误与中间件

解决方案:使用双括号:create<T>()() 而非 create<T>()

问题:持久化中间件导致水合错误

解决方案:实现 _hasHydrated 标志模式(见问题 #1)

问题:动作不在 Redux DevTools 中显示

解决方案:将动作名称作为 set 的第三个参数传递:set(newState, undefined, 'actionName')

问题:存储状态意外重置

解决方案:检查是否使用 HMR(热模块替换) - 在开发中,Zustand 会在模块重载时重置


完整设置检查清单

使用此检查清单验证您的 Zustand 设置:

  • [ ] 安装了 zustand@5.0.8 或更高版本
  • [ ] 使用适当的 TypeScript 类型创建存储
  • [ ] 使用了 create<T>()() 双括号语法
  • [ ] 在组件中测试了选择器函数
  • [ ] 验证了组件仅在选择的状态更改时重渲染
  • [ ] 如果使用持久化:配置了唯一存储名称
  • [ ] 如果使用持久化:为 Next.js 实现了水合检查
  • [ ] 如果使用 devtools:为调试命名了动作
  • [ ] 如果使用切片:为每个切片正确类型化了 StateCreator
  • [ ] 所有动作都是纯函数
  • [ ] 无直接状态突变
  • [ ] 存储在生产构建中工作

有问题?问题?

  1. 查看 references/typescript-patterns.md 获取 TypeScript 帮助
  2. 查看 references/nextjs-hydration.md 获取 Next.js 问题
  3. 查看 references/middleware-guide.md 获取持久化/devtools 帮助
  4. 官方文档:https://zustand.docs.pmnd.rs/
  5. GitHub 问题:https://github.com/pmndrs/zustand/issues