name: zustand-state-management description: Zustand 用于 React 的 TypeScript 状态管理。适用于全局状态、Redux/Context API 迁移、localStorage 持久化、切片模式、开发工具、Next.js SSR,或遇到水合错误、TypeScript 推理问题、持久化中间件问题、无限渲染循环。
关键词:zustand, 状态管理, React状态, TypeScript状态, 持久化中间件, 开发工具, 切片模式, 全局状态, React钩子, 创建存储, useBoundStore, StateCreator, 水合错误, 文本内容不匹配, 无限渲染, localStorage, sessionStorage, immer中间件, 浅相等, 选择器模式, zustand v5 license: MIT
Zustand 状态管理
状态: 生产就绪 ✅ 最后更新: 2025-11-21 最新版本: zustand@5.0.8 依赖: React 18+, TypeScript 5+
快速开始(3分钟)
1. 安装 Zustand
bun add zustand # 推荐
# 或:npm install zustand
# 或:yarn add zustand
为什么选择 Zustand?
- 最小API:只需学习1个函数(
create) - 无样板代码:无需提供者、减速器或操作
- TypeScript优先:优秀的类型推断
- 快速:细粒度订阅防止不必要的重新渲染
- 灵活:中间件支持持久化、开发工具等
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} 在这里...</h1>
}
function Controls() {
const increase = useBearStore((state) => state.increase)
return <button onClick={() => increase(1)}>添加熊</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 })) - 导致无限渲染
❌ 对多个存储使用相同的存储名称 - 导致数据冲突
❌ 在水合检查期间访问 localStorage
❌ 使用 Zustand 处理服务器状态 - 使用 TanStack Query 代替
❌ 直接导出存储实例 - 始终导出钩子
已知问题预防(5个问题)
| 问题 | 错误 | 快速修复 |
|---|---|---|
| #1 水合不匹配 | “文本内容不匹配” | 使用 _hasHydrated 标志 + onRehydrateStorage |
| #2 TypeScript 推断 | 类型与中间件破坏 | 使用 create<T>()() 双括号 |
| #3 导入错误 | “createJSONStorage 未导出” | 升级到 zustand@5.0.8+ |
| #4 无限循环 | 浏览器冻结 | 使用 shallow 或分离选择器 |
| #5 切片类型 | StateCreator 类型失败 | 显式 StateCreator<Combined, [], [], Slice> |
最关键 - TypeScript 双括号:
// ❌ 错误:create<T>((set) => ...)
// ✅ 正确:create<T>()((set) => ...)
参见:references/known-issues.md 获取完整解决方案和代码示例。
中间件配置
import { create } from 'zustand'
import { devtools, persist } from 'zustand/middleware'
const useStore = create<MyStore>()(
devtools(
persist(
(set) => ({ /* 存储定义 */ }),
{ name: 'my-storage' },
),
{ name: 'MyStore' },
),
)
| 中间件 | 目的 | 导入 |
|---|---|---|
persist |
localStorage/sessionStorage | zustand/middleware |
devtools |
Redux DevTools 集成 | zustand/middleware |
immer |
可变更新语法 | zustand/middleware/immer |
顺序重要:devtools(persist(...)) 在 DevTools 中显示持久化操作。
参见:references/middleware-guide.md 获取完整中间件文档。
常见模式
| 模式 | 用例 | 关键技术 |
|---|---|---|
| 计算值 | 派生数据 | 在选择器中计算:state.items.length |
| 异步操作 | API 调用 | set({ isLoading: true }) + try/catch |
| 重置存储 | 注销、表单清除 | set(initialState) |
| 带参数的选择器 | 动态访问 | state.todos.find(t => t.id === id) |
| 多个存储 | 关注点分离 | 创建单独的 create() 调用 |
参见:references/common-patterns.md 获取完整实现。
高级主题
| 主题 | 用例 | 关键API |
|---|---|---|
| Vanilla 存储 | 非React、测试 | createStore() 来自 zustand/vanilla |
| 自定义中间件 | 日志记录、时间戳 | 包装 StateCreator |
| Immer | 可变更新语法 | immer() 中间件 |
| 订阅 | 副作用 | store.subscribe() |
参见:references/advanced-topics.md 获取完整实现。
捆绑资源
| 类型 | 文件 |
|---|---|
| 模板 | basic-store.ts, typescript-store.ts, persist-store.ts, slices-pattern.ts, devtools-store.ts, nextjs-store.ts, computed-store.ts, async-actions-store.ts |
| 参考资料 | middleware-guide.md, typescript-patterns.md, nextjs-hydration.md, migration-guide.md, known-issues.md, common-patterns.md, advanced-topics.md |
何时加载参考资料
| 参考资料 | 加载时机… |
|---|---|
known-issues.md |
调试水合、TypeScript、无限循环或切片错误时 |
common-patterns.md |
实现计算值、异步操作、重置模式时 |
advanced-topics.md |
使用Vanilla存储、自定义中间件、Immer、订阅时 |
middleware-guide.md |
配置持久化、开发工具或组合中间件时 |
typescript-patterns.md |
复杂类型推断问题、StateCreator问题时 |
nextjs-hydration.md |
Next.js SSR/水合问题时 |
migration-guide.md |
从Redux、Context API或Zustand v4迁移时 |
快速故障排除
| 问题 | 解决方案 |
|---|---|
| 存储更新不触发重新渲染 | 使用选择器:useStore(state => state.value) 而不是解构 |
| 中间件导致的TypeScript错误 | 使用 create<T>()() 双括号 |
| 持久化导致的水合错误 | 实现 _hasHydrated 标志模式 |
| DevTools中不显示操作 | 传递操作名称:set(newState, undefined, 'actionName') |
| 存储意外重置 | HMR在开发中导致重置 |
依赖
{ "dependencies": { "zustand": "^5.0.8", "react": "^18.0.0+" } }
兼容性:React 18+, React 19, TypeScript 5+, Next.js 14+, Vite 5+
官方文档:https://zustand.docs.pmnd.rs/ | GitHub:https://github.com/pmndrs/zustand