关于 nextjs-zustand
面向 Next.js 16 App Router 应用中使用 Zustand v5 管理客户端状态的技能,重点介绍基于 Context 的请求隔离 Store、SSR 水合、中间件和文件组织。
主要功能
- 01介绍 Zustand v5 Store 创建语法
- 02使用基于 Context 的 Store 隔离请求
- 03涵盖中间件组合
- 04处理持久化与 SSR 水合
- 05按功能模块组织 Store
适用场景
- 01管理多个客户端组件共享的状态
- 02处理 UI 状态持久化与水合
- 03组织 App Router 项目的 Store
- 04避免选择器引发不必要的重渲染
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ fusengine/ claude-plugins --skill nextjs-zustand安装后在 Claude Code 中输入/nextjs-zustand确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/auto-selectors.mdhydration.mdinstallation.mdmiddleware.mdmigration-v5.mdnextjs-integration.mdreset-state.mdslices.mdstore-patterns.mdsubscribe-api.mdtesting.mdtypescript.md详细介绍
涵盖 persist 中间件的 skipHydration,以及在 useEffect 中手动重新水合以避免 SSR 不匹配;用于数组/对象选择器的 useShallow,以及 SOLID 文件组织(store.ts、store-provider.tsx、use-store.ts)。明确说明 Zustand 仅用于客户端组件——绝不用于服务端组件(应直接在那里获取数据),也绝不用于持久化身份验证令牌(请使用 httpOnly Cookie)。不涵盖服务端状态缓存(参见 nextjs-tanstack-query)。
Next.js 16 中的 Zustand
基于 React 18+ useSyncExternalStore 的精简、可扩展状态管理。
Agent 工作流(必须遵循)
在进行任何实现之前,并行启动 3 个 Agent,每个各调用一次 Agent,并指定一个 name:
- fuse-ai-pilot:explore-codebase - 分析现有存储和状态模式
- fuse-ai-pilot:research-expert - 通过 Context7/Exa 验证最新的 Zustand v5 文档
- mcp__context7__query-docs - 检查中间件和 TypeScript 模式
实现后,运行 fuse-ai-pilot:sniper 进行验证。
概述
适用场景
- 在 Next.js App Router 应用中管理客户端状态
- 仅需在客户端组件之间共享全局状态
- 将状态持久化到 localStorage/sessionStorage
- 构建 UI 状态(模态框、侧边栏、主题、购物车)
- 用于替代复杂状态管理中的 React Context
为什么选择 Zustand v5
| 特性 | 优势 |
|---|---|
| 极简 API | 简单的 create() 函数,无需样板代码 |
| 原生支持 React 18 | 使用 useSyncExternalStore,无需垫片 |
| TypeScript 优先 | 柯里化模式可实现完整类型推断 |
| 中间件栈 | 可组合使用 devtools、persist、immer |
| 包体积 | 压缩后约 2KB,是最小的状态库 |
| 无需 Provider | 可直接访问存储,无需 Context 包装器 |
关键规则
- 仅限客户端组件 - 绝不在服务端组件中使用 Zustand
- App Router 使用 Context 模式 - 避免使用全局存储(隔离请求)
- 数组/对象使用 useShallow - 避免不必要的重新渲染
- persist 使用 skipHydration - SSR 兼容所必需
- v5 柯里化语法 -
create<State>()((set) => ({...})) - SOLID 路径 - 存储放在
modules/[feature]/src/stores/
SOLID 架构
模块结构
按功能模块组织存储:
modules/cores/stores/- 共享存储(主题、UI)modules/auth/src/stores/- 身份验证状态modules/cart/src/stores/- 购物车状态modules/[feature]/src/interfaces/- 存储类型
文件组织
| 文件 | 用途 |
|---|---|
store.ts | 使用 create() 创建存储(最多 40 行,遵循所有者规则) |
store.interface.ts | TypeScript 接口 |
store-provider.tsx | Context Provider(App Router) |
use-store.ts | 带选择器的自定义 Hook |
核心概念
创建存储(v5 语法)
TypeScript 类型推断需要使用双括号。柯里化模式可确保完整的类型安全。
基于 Context 的存储
对于 Next.js App Router,应将存储封装在 Context 中,以防止请求间共享。使用 zustand/vanilla 中的 createStore,并通过 useRef 进行初始化。
中间件组合
按以下顺序叠加中间件:devtools → persist → immer。顺序会影响 TypeScript 类型。
水合处理
在 persist 中间件中使用 skipHydration: true。在 useEffect 中手动重新水合,以避免 SSR 不匹配。
参考指南
| 需求 | 参考 |
|---|---|
| 初始设置 | installation.md |
| Store 模式 | store-patterns.md |
| SSR/水合 | hydration.md |
| 中间件 | middleware.md |
| Next.js App Router | nextjs-integration.md |
| TypeScript | typescript.md |
| Slices 模式 | slices.md |
| 自动选择器 | auto-selectors.md |
| 重置状态 | reset-state.md |
| 订阅 API | subscribe-api.md |
| 测试 | testing.md |
| 从 v4 迁移到 v5 | migration-v5.md |
最佳实践
- 选择器模式 - 始终使用
useStore((s) => s.field)以提升性能 - useShallow - 使用
useShallow包装数组/对象选择器,避免重新渲染 - 分离 Store - 每个领域使用一个 Store(auth、cart、ui、theme)
- 将服务端数据存放在其他位置 - 使用 TanStack Query 管理服务端状态
- 仅在开发环境使用 DevTools - 使用 process.env 检查包裹 devtools
- 精简持久化内容 - 只持久化必要字段,绝不持久化令牌
禁止的模式
| 模式 | 原因 | 替代方案 |
|---|---|---|
| 在 App Router 中使用全局 Store | 用户之间共享请求 | 基于 Context 的 Store |
| 在 Server Components 中使用 Zustand | RSC 中没有 React hooks | 直接获取数据 |
| 持久化认证令牌 | 安全漏洞 | httpOnly cookies |
| 对象选择器未使用 useShallow | 过度重新渲染 | useShallow(selector) |
| v4 语法 | TypeScript 类型推断失效 | v5 柯里化语法 create<T>()() |