关于 state-management
state-management 面向需要决定前端状态归属的团队,按生命周期分类状态并推荐最小适用的管理者,避免一开始就陷入库的选择。
主要功能
- 01按生命周期分类状态
- 02选择范围最小的适用管理者
- 03区分 URL、表单、服务端与客户端状态
- 04按需比较客户端状态库
- 05指出常见的归属反模式
- 06将相邻任务转交其他技能
适用场景
- 01决定共享 UI 状态的归属
- 02确定筛选条件和分页的存放位置
- 03分离获取的数据与客户端工作流
- 04将状态故障转交调试处理
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ akillness/ jeo-skills --skill state-management安装后在 Claude Code 中输入/state-management确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
SKILL.toonevals/evals.jsonreferences/decision-matrix.mdhandoff-boundaries.mdownership-packets-and-route-outs.md详细介绍
状态管理
当真正的问题是以下内容时,使用此技能:
这是什么状态数据包、应该由谁负责,以及应该避免哪个看似诱人的错误负责人?
不要从争论库开始。先指出一个主要数据包,然后选择生命周期与之匹配的最小负责人。
处理混合或模糊的请求前,请阅读 references/ownership-packets-and-route-outs.md。
当数据包明确且需要比较可能的负责人时,请阅读 references/decision-matrix.md。
当请求实际上可能属于 react-best-practices、api-design、debugging、design-system、design-system 或 responsive-design 时,请阅读 references/handoff-boundaries.md。
何时使用此技能
- 判断前端问题属于本地 UI 状态、共享子树状态、URL/导航状态、表单状态、服务端状态,还是长期客户端工作流状态。
- 将“我们需要状态管理”之类的模糊请求转化为具体的状态归属建议。
- 在不把 Context、Zustand、Redux Toolkit、Jotai、TanStack Query 或路由原生状态管理视为可互换方案的前提下,从中做出选择。
- 在实现开始前,阻止“一个 Store 管所有事”的提议。
- 为实现或评审撰写简短的状态归属说明。
何时不使用此技能
- 主要任务是重渲染频繁、hydration 不匹配、瀑布请求或客户端/服务端性能行为 → 使用
react-best-practices。 - 主要任务是变更形态、后端归属或乐观写入 API 设计 → 使用
api-design。 - 主要任务是调试过期闭包、竞态、失效的乐观更新或现有状态 bug → 使用
debugging。 - 主要任务是受控/非受控组件 API、基础组件、插槽或可复用组件契约 → 使用
design-system。 - 主要任务是视觉系统治理或共享偏好策略 → 使用
design-system。 - 主要任务是视口适配或布局坍塌 → 使用
responsive-design。 - 架构选择已经确定,用户只需要代码 → 直接实现,不要重新运行选择流程。
说明
步骤 1:选择一个主要数据包
在指出任何工具之前,先选择主要数据包:
- local-ui — 开关、打开的面板、行内编辑状态、活动行、单个功能的临时状态
- shared-subtree — 主题、身份验证快照、区域设置、功能标志、中等规模的外壳状态
- url-navigation — 筛选条件、排序顺序、分页、选中标签页、可深度链接的视图状态
- form-lifecycle — 脏状态/已触碰状态、验证、提交生命周期、异步提交错误
- server-state — 获取的数据、缓存、失效、重新验证、乐观服务端更新
- client-workflow — 跨页面草稿、撤销、多步骤协调、离线编辑、长期工作流状态
可选:列出一两个次要数据包,但必须指定一个主要数据包。
快速参考:
Primary packet: server-state
Secondary packets: url-navigation, local-ui
Decision pressure: shareable filters + remote list freshness
步骤 2:选择最小负责人
使用与数据包匹配的最窄范围负责人:
- local-ui → 本地状态、提升状态或
useReducer - shared-subtree → 当一个规模适中的组件树需要共享唯一事实来源时,使用 Context
- url-navigation → 当刷新、深度链接和前进/后退应自然生效时,使用路由器/搜索参数
- form-lifecycle → 由表单层负责,而非应用范围的全局状态
- server-state → 当缓存/重新验证规则占主导时,使用 TanStack Query 或路由器原生数据 API
- client-workflow → 仅在区分前述数据包之后,再使用 Zustand、Redux Toolkit、Jotai 或显式状态机建模
规则:如果状态实际上具有路由特征或服务端特征,就不要仅仅因为已有通用客户端 Store 而把它藏进去。
步骤 3:检查反模式
明确指出最可能的错误归属:
- 用一个通用存储管理彼此无关的生命周期
- 在没有离线或工作流需求的情况下,将服务器缓存镜像到客户端存储
- 将可分享的筛选条件或标签页隐藏在 URL 之外
- 将表单的 dirty/touched/submit 生命周期推入全局应用状态
- 把所有缓慢或有 Bug 的 UI 问题都当作状态架构问题
好的回答会同时指出选定的归属和容易误选的归属。
步骤 4:仅当仍有客户端工作流类别时,比较客户端存储选项
将本地、共享、URL 和表单类别分开后:
- Context:真正的问题是需要在中等规模的组件树中共享访问能力
- Zustand:需要低仪式感的跨组件或跨页面工作流状态
- Redux Toolkit:工作流彼此耦合、事件繁多,或需要更严格的约定和可审计性
- Jotai:细粒度的原子组合确实符合应用的思维模型
- 状态机/显式工作流建模:转换、守卫和多步骤协调占主导地位
对于已经归属 URL、表单或服务器的类别,不要比较存储方案。
步骤 5:直接说明混合架构
健康的组合很常见:
- URL 状态 + 查询/路由器数据 + 本地 UI 状态
- Context + 查询/路由器数据
- Zustand + 查询/路由器数据
- Redux Toolkit + 查询/路由器数据
- 表单状态 + URL 状态 + 查询/路由器数据
如果请求要求使用一个通用存储,请说明将不同生命周期压平到一起会带来哪些问题。
步骤 6:立即将相邻工作转交出去
只在选择归属时留在这里。
出现以下下一步工作时,应转交出去:
react-best-practices:重渲染/水合/瀑布流/性能问题api-design:变更契约或后端/客户端职责debugging:已经出错的状态行为design-system:组件 API 归属design-system:系统级偏好/治理规则responsive-design:布局/视口行为
步骤 7:给出一份归属简报
使用以下格式:
Primary packet:
Secondary packets:
Recommended owners:
- ...
Why this is the smallest viable split:
- ...
Avoid:
- ...
Route-out note:
- ...
Next implementation step:
- ...
保持简短。如果诚实的答案是“少做一些,让状态留在更接近其所在位置的地方”,就直接这么说。
输出格式
返回简明的归属建议,包含:
- 一个主要类别和任意次要类别
- 各类别选定的归属
- 一段理由
- 明确指出要避免的反模式
- 如果下一步应由其他技能负责,注明转交说明
示例
示例 1:仪表板数据、筛选条件和模态框
输入:“这个仪表板的项目数据、筛选条件和模态框状态,应该用 Zustand 还是 TanStack Query?”
理想输出形式:
- 主要类别 = 服务器状态
- 次要类别 = URL 导航、本地 UI
- 项目数据 = TanStack Query 或路由器原生数据归属
- 筛选条件 = 若可分享/可加入书签,则使用 URL/搜索参数
- 模态框状态 = 本地状态;只有在需要协调相距较远的组件分支时,才使用轻量客户端状态
- 避免在没有离线/工作流需求的情况下,将获取的实体镜像到 Zustand
示例 2:通过属性逐层传递主题和身份验证信息
输入:“主题和身份验证信息需要经过五层传递。我们需要 Redux 吗?”
理想输出形式:
- 主要类别 = 共享子树
- 优先推荐 Context
- 除非还存在范围更广、彼此耦合的工作流状态,否则不建议使用 Redux
示例 3:将筛选条件和加载器数据存储在 Zustand 中的 React Router 应用
输入:“我们的 React Router 应用将筛选条件和加载器数据存储在 Zustand 中。这样可以吗?”
理想输出形式:
- 主要类别 = URL 导航或服务器状态,而非客户端工作流
- 将可分享的筛选条件移至 URL/搜索参数
- 将加载器/操作数据保留在路由器原生数据归属中,除非确实需要单独的缓存
- 仅将 Zustand 用于剩余的工作流协调
示例 4:乐观更新失效
输入:“状态代码有缺陷,乐观更新也失效了——下一步该由哪个技能负责?”
良好的输出形式:
- 指出相比于确定责任归属,优先诊断当前故障
- 将下一步交给
debugging - 如果真正的争议在于乐观写入约定,则提及
api-design
最佳实践
- 先选择数据包,再选择工具。
- 选择职责范围最小且可行的负责人。
- 将 URL、表单、服务端和客户端工作流的生命周期分开处理,除非有充分理由不这样做。
- 明确指出不合适的负责人。
- 将混合责任视为常态,而不是未能实现标准化。
- 最后给出一份简短说明,让实现者或审核者可以立即采取行动。