mcpField
S
state-management

state-management

github.com/akillness开发52365 次安装1 天前更新
代码仓库

关于 state-management

state-management 面向需要决定前端状态归属的团队,按生命周期分类状态并推荐最小适用的管理者,避免一开始就陷入库的选择。

主要功能

  1. 01按生命周期分类状态
  2. 02选择范围最小的适用管理者
  3. 03区分 URL、表单、服务端与客户端状态
  4. 04按需比较客户端状态库
  5. 05指出常见的归属反模式
  6. 06将相邻任务转交其他技能

适用场景

  1. 01决定共享 UI 状态的归属
  2. 02确定筛选条件和分页的存放位置
  3. 03分离获取的数据与客户端工作流
  4. 04将状态故障转交调试处理

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/akillness/jeo-skills --skill state-management

安装后在 Claude Code 中输入/state-management确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

SKILL.toon
evals/
evals.json
references/
decision-matrix.md
handoff-boundaries.md
ownership-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. 一个主要类别和任意次要类别
  2. 各类别选定的归属
  3. 一段理由
  4. 明确指出要避免的反模式
  5. 如果下一步应由其他技能负责,注明转交说明

示例

示例 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

最佳实践

  1. 先选择数据包,再选择工具。
  2. 选择职责范围最小且可行的负责人。
  3. 将 URL、表单、服务端和客户端工作流的生命周期分开处理,除非有充分理由不这样做。
  4. 明确指出不合适的负责人。
  5. 将混合责任视为常态,而不是未能实现标准化。
  6. 最后给出一份简短说明,让实现者或审核者可以立即采取行动。

参考资料

评论

支持 Markdown