关于 zustand-state-management
面向 React 项目的 Zustand 状态管理 Claude 技能,提供 TypeScript、持久化、中间件和 Next.js 水合指导,并附带模板与故障排查说明。
主要功能
- 01基础与类型化 Store 模式
- 02选择器与不可变更新
- 03持久化和 DevTools 中间件
- 04Next.js 水合指导
- 05模板与故障排查
适用场景
- 01在 React 应用中创建 Zustand Store
- 02让偏好设置在重新加载后保留
- 03排查 TypeScript 或渲染循环问题
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ jackspace/ claudeskillz --skill zustand-state-management安装后在 Claude Code 中输入/zustand-state-management确认已加载。
附带文件README.mdSKILL.json
详细介绍
Zustand 状态管理
状态:已可用于生产环境 ✅ 最后更新:2025-10-24 最新版本:[email protected] 依赖项:React 18+、TypeScript 5+
快速入门(3 分钟)
1. 安装 Zustand
npm install zustand
# or
pnpm add zustand
# or
yarn add zustand
为什么选择 Zustand?
- API 精简:只需学习 1 个函数(
create) - 无样板代码:无需 Provider、reducer 或 action
- TypeScript 优先:类型推断出色
- 速度快:细粒度订阅可避免不必要的重新渲染
- 灵活:提供用于持久化、开发者工具等功能的中间件
2. 创建你的第一个 Store(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. 在组件中使用 Store
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 Provider
- 选择器函数可提取特定的状态切片
3 种模式的设置流程
模式 1:基础 Store(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 Store(推荐)
适用于需要类型安全的生产应用:
import { create } from 'zustand'
// Define store interface
interface CounterStore {
count: number
increment: () => void
decrement: () => void
}
// Create typed store
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:持久化 Store
适用于需要在页面重新加载后保留的状态:
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', // unique name in localStorage
storage: createJSONStorage(() => localStorage), // optional: defaults to localStorage
},
),
)
这很重要,因为:
- 状态会自动保存到 localStorage
- 页面重新加载后会恢复状态
- 也支持 sessionStorage
- 自动处理序列化
关键规则
始终遵循
✅ 在 TypeScript 中使用中间件时,使用 create<T>()()(双括号)
✅ 为状态和操作分别定义接口
✅ 使用选择器函数提取特定的状态切片
✅ 对派生状态使用带更新函数的 set:set((state) => ({ count: state.count + 1 }))
✅ 为 persist 中间件存储键使用唯一名称
✅ 使用 hasHydrated 标志模式处理 Next.js 水合
✅ 选择多个值时使用 shallow
✅ 保持操作纯净(除更新状态外,不执行副作用)
绝不这样做
❌ 在 TypeScript 中使用 create<T>(...)(单括号)——会破坏中间件类型
❌ 直接修改状态:set((state) => { state.count++; return state })——应使用不可变更新
❌ 在选择器中创建新对象:useStore((state) => ({ a: state.a }))——会导致无限次渲染
❌ 多个 Store 使用相同的存储名称——会造成数据冲突
❌ 未进行水合检查就在 SSR 期间访问 localStorage
❌ 使用 Zustand 管理服务端状态——应改用 TanStack Query
❌ 直接导出 Store 实例——始终导出 Hook
预防已知问题
此技能可预防 5 个有记录的问题:
问题 #1:Next.js 水合不匹配
错误:"Text content does not match server-rendered HTML" 或 "Hydration failed"
来源:
- DEV Community:Next.js 中的 Persist 中间件
- GitHub Discussions #2839
发生原因: Persist 中间件会在客户端读取 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)
},
},
),
)
// In component
function MyComponent() {
const hasHydrated = useStore((state) => state._hasHydrated)
if (!hasHydrated) {
return <div>Loading...</div>
}
// Now safe to render with persisted state
return <ActualContent />
}
问题 #2:缺少 TypeScript 双括号
错误:类型推断失败,StateCreator 类型与中间件不兼容
发生原因:
中间件需要使用柯里化语法 create<T>()(),才能与 TypeScript 类型推断配合工作。
预防方法:
// ❌ WRONG - Single parentheses
const useStore = create<MyStore>((set) => ({
// ...
}))
// ✅ CORRECT - Double parentheses
const useStore = create<MyStore>()((set) => ({
// ...
}))
规则:在 TypeScript 中始终使用 create<T>()(),即使没有使用中间件也一样(可面向未来)。
问题 #3:Persist 中间件导入错误
错误:"Attempted import error: 'createJSONStorage' is not exported from 'zustand/middleware'"
来源:GitHub Discussion #2839
发生原因: 导入路径错误,或 zustand 与构建工具版本不匹配。
预防方法:
// ✅ CORRECT imports for v5
import { create } from 'zustand'
import { persist, createJSONStorage } from 'zustand/middleware'
// Verify versions
// [email protected] includes createJSONStorage
// [email protected] uses different API
// Check your package.json
// "zustand": "^5.0.8"
问题 #4:无限渲染循环
错误:组件无限重新渲染,浏览器冻结
来源:GitHub 讨论 #2642
原因: 在选择器中创建新的对象引用会导致 Zustand 认为状态已更改。
预防措施:
import { shallow } from 'zustand/shallow'
// ❌ WRONG - Creates new object every time
const { bears, fishes } = useStore((state) => ({
bears: state.bears,
fishes: state.fishes,
}))
// ✅ CORRECT Option 1 - Select primitives separately
const bears = useStore((state) => state.bears)
const fishes = useStore((state) => state.fishes)
// ✅ CORRECT Option 2 - Use shallow for multiple values
const { bears, fishes } = useStore(
(state) => ({ bears: state.bears, fishes: state.fishes }),
shallow,
)
问题 #5:切片模式的 TypeScript 复杂性
错误:StateCreator 类型无法推断,复杂的中间件类型会出错
来源:官方切片模式指南
原因: 组合多个切片时,需要显式标注类型以确保中间件兼容性。
预防措施:
import { create, StateCreator } from 'zustand'
// Define slice types
interface BearSlice {
bears: number
addBear: () => void
}
interface FishSlice {
fishes: number
addFish: () => void
}
// Create slices with proper types
const createBearSlice: StateCreator<
BearSlice & FishSlice, // Combined store type
[], // Middleware mutators (empty if none)
[], // Chained middleware (empty if none)
BearSlice // This slice's type
> = (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 })),
})
// Combine slices
const useStore = create<BearSlice & FishSlice>()((...a) => ({
...createBearSlice(...a),
...createFishSlice(...a),
}))
中间件配置
Persist 中间件(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 }), // Only persist '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', // Action name in DevTools
),
}),
{ name: 'CounterStore' }, // Store name in DevTools
),
)
组合多个中间件
import { create } from 'zustand'
import { devtools, persist } from 'zustand/middleware'
const useStore = create<MyStore>()(
devtools(
persist(
(set) => ({
// store definition
}),
{ name: 'my-storage' },
),
{ name: 'MyStore' },
),
)
顺序很重要:devtools(persist(...)) 会在 DevTools 中显示持久化操作。
常见模式
模式:计算值/派生值
interface StoreWithComputed {
items: string[]
addItem: (item: string) => void
// Computed in selector, not stored
}
const useStore = create<StoreWithComputed>()((set) => ({
items: [],
addItem: (item) => set((state) => ({ items: [...state.items, item] })),
}))
// Use in component
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 })
}
},
}))
模式:重置 Store
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
),
})),
}))
// Use with parameter
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 Store 示例typescript-store.ts- 类型标注完善的 TypeScript Storepersist-store.ts- 带迁移功能的 localStorage 持久化slices-pattern.ts- 模块化 Store 组织方式devtools-store.ts- Redux DevTools 集成nextjs-store.ts- 支持水合且兼容 SSR 的 Next.js Storecomputed-store.ts- 派生状态模式async-actions-store.ts- 带加载状态的异步操作
使用示例:
# Copy template to your project
cp ~/.claude/skills/zustand-state-management/templates/typescript-store.ts src/store/
适用场景:
- 快速构建原型时,使用
basic-store.ts - 大多数生产应用中,使用
typescript-store.ts - 状态需要在页面重新加载后保留时,使用
persist-store.ts - 大型复杂 Store(100 行以上)中,使用
slices-pattern.ts - 在使用 SSR 的 Next.js 项目中,使用
nextjs-store.ts
参考文档(references/)
适用于复杂场景的深入文档:
middleware-guide.md- 完整的中间件文档(persist、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 Store(不使用 React)
import { createStore } from 'zustand/vanilla'
const store = createStore<CounterStore>()((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
}))
// Subscribe to changes
const unsubscribe = store.subscribe((state) => {
console.log('Count changed:', state.count)
})
// Get current state
console.log(store.getState().count)
// Update state
store.getState().increment()
// Cleanup
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)
}
// Use custom middleware
const useStore = create<MyStore>()(
logger((set) => ({
// store definition
}), '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) => {
// Mutate directly with Immer
state.todos.push({ id: Date.now().toString(), text })
}),
})),
)
依赖项
必需:
[email protected]- 状态管理库[email protected]+- React 框架
可选:
@types/node- 用于 TypeScript 路径解析immer- 用于可变更新语法- Redux DevTools Extension - 用于 devtools 中间件
官方文档
- Zustand:https://zustand.docs.pmnd.rs/
- GitHub:https://github.com/pmndrs/zustand
- TypeScript 指南:https://zustand.docs.pmnd.rs/guides/typescript
- 切片模式:https://github.com/pmndrs/zustand/blob/main/docs/guides/slices-pattern.md
- Context7 库 ID:
/pmndrs/zustand
软件包版本(已于 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+ ✅
故障排除
问题:Store 更新不会触发重新渲染
解决方案:确保使用选择器函数,而不是解构:const bears = useStore(state => state.bears),而不是 const { bears } = useStore()
问题:使用中间件时出现 TypeScript 错误
解决方案:使用双括号:create<T>()(),而不是 create<T>()
问题:Persist 中间件导致 hydration 错误
解决方案:实现 _hasHydrated 标志模式(参见 Issue #1)
问题:操作未显示在 Redux DevTools 中
解决方案:将操作名称作为第三个参数传给 set:set(newState, undefined, 'actionName')
问题:Store 状态意外重置
解决方案:检查是否使用了 HMR(热模块替换)——开发环境中模块重新加载时 Zustand 会重置
完整设置检查清单
使用此检查清单验证你的 Zustand 设置:
- 已安装
[email protected]或更高版本 - 已使用正确的 TypeScript 类型创建 Store
- 已使用
create<T>()()双括号语法 - 已在组件中测试选择器函数
- 已验证组件仅在所选状态发生变化时重新渲染
- 如果使用 persist:已配置唯一的存储名称
- 如果使用 persist:已为 Next.js 实现 hydration 检查
- 如果使用 devtools:已为调试命名操作
- 如果使用 slices:已为每个 slice 正确设置
StateCreator类型 - 所有操作都是纯函数
- 没有直接修改状态
- Store 在生产构建中正常工作
有疑问?遇到问题?
- 查看 references/typescript-patterns.md 获取 TypeScript 帮助
- 查看 references/nextjs-hydration.md 获取 Next.js 问题的帮助
- 查看 references/middleware-guide.md 获取 persist/devtools 相关帮助
- 官方文档:https://zustand.docs.pmnd.rs/
- GitHub 问题:https://github.com/pmndrs/zustand/issues