SuperAPI AI 工具集

state-management-zustand

前端与建站

查看项目

更新于 10-18 18:02

技能介绍

用途与定位

该技能面向使用 React Native/Expo 与 Zustand 5 的客户端状态管理场景,目标是把客户端状态约束为“薄 store + 纯 reducer + 先写盘后发布”的结构。它不提供运行时库,而是一套规则、模板和检查脚本,用于在项目中添加 store、action、selector,或从 Redux、Context、MobX、persist+AsyncStorage 迁移时保持一致性。

主要能力

  • store 结构约束:每个领域一个 store,由 create<Domain>Store(deps) 工厂生成 vanilla store(createStore 来自 zustand/vanilla),在根布局的 useState 初始化器与 test/test-app.tsx 中各创建一次;禁止 create from zustand、模块级 store(除 contracts/stores.json 中列出的 OS 镜像 store),禁止在 feature/UI 代码中调用工厂。
  • 持久化顺序:变更先交给 reducer(被拒绝或无变化返回同一对象,不写不渲染),通过工厂接收的 port 同步写入,检查写入结果,最后才 set。写入失败只发布失败(返回 Result 或 …Failure 字段),屏幕不显示未落盘的值。服务产生的状态(如 Premium 权益)也必须通过带写入的 store 方法进入,而不是仅发布型 dispatch。
  • 纯 reducer 与用例表:<domain>Reducer(state, action) 不导入运行时代码、不修改状态、不读时钟或随机数(时间与 id 由 action 传入),无变化返回同一对象;<name>-reducer-cases.ts 以纯数据承载规则(至少一个 same: true 用例),由测试与 check-reducers 执行。
  • 选择器规则:选择器返回原始值或状态中已有引用;构造值须经 useShallow;禁止无选择器的 store hook 和相等性参数。Zustand 5 中每次返回新引用的选择器会触发 “Maximum update depth exceeded”。
  • 跨 store 写入:通过 updateAndPublish(stores, () => tasks.removeList(id)) 作为一次事务,并在 contracts/stores.json 的 crossStoreWrites 中登记;避免两次 store 调用或裸写导致半更新。
  • 状态归属:服务端数据留在 TanStack Query 缓存,路由参数归 Expo Router,store 只持有客户端状态(本地行、UI 选择、偏好、会话)。store 内禁止 fetch、React Query、API 客户端、async/await、.then、zustand persist 中间件、AsyncStorage/MMKV/SQLite 等。
  • 可撤销会话:每次会话一个 store 加纯 session reducer;past 留在内存用于即时撤销;写入形式保存 start、命令日志和快照;恢复时重放日志重建撤销历史,重放不一致时保留快照,并以暂停状态返回;同样先持久化再发布。
  • 模板与示例:templates/src/stores/ 提供 lists/tasks 的 reducer、用例表、选择器、store 及测试;templates/src/stores/session/ 提供可撤销会话;examples/use-tasks-model.ts、examples/app-root.tsx、examples/app-providers.tsx 展示组合方式。
  • 检查脚本:scripts/check-stores.mjs 对 src/ 做静态规则检查(支持 --src-dir、--stores-dir、--contract 选项);scripts/check-reducers.mjs 在 Node 中运行每个 reducer 的用例表和通用不变量,以及 session reducer 规则。两者输出 RESULT: PASS 表示通过,FAIL 行会指出文件、规则和修复方式。

使用方式

  1. 阅读 references/store-pattern.md;决定状态归属或迁移时阅读 references/state-homes.md;构建撤销功能前阅读 references/undoable-session.md。
  2. 检查前置条件:src/core/result.ts、数据层(persistence-and-migrations 的仓库或 document service)、组合根(architecture-and-boundaries)。
  3. 从仓库根安装固定版本:npm install -E zustand@5.0.15(多包时在 app workspace 用 -w apps/<app> 执行)。
  4. 复制 templates/src/stores/(先不含 session/)、templates/contracts/,将 Tasks 域替换为应用领域;在 AppRoot 的 startApp 中、数据层和错误接收器之后接入 createStores;更新 test/test-app.tsx;填写 contracts/stores.json。
  5. 新增 action 时,先在 <name>-reducer-cases.ts 添加会失败的用例,再添加 union 成员与 reducer case,最后按规则 2 的顺序添加 store 方法。
  6. 新增读取时,添加或复用选择器;构造值经 useShallow 读取;仅在事件处理器中修改状态。
  7. 跨 store 写入时,在 crossStoreWrites 中登记,通过 updateAndPublish 调用,并为每个持有行的新 store 添加 reload 和 updateAndPublish 中的一行。
  8. 撤销功能:复制 templates/src/stores/session/ 与 templates/test/stores/;feature 在 useState 初始化器中为每次会话创建一个 session store,持久化回调通过仓库写入 toSavedSession(...);用 restoreSession 恢复(返回暂停状态)。
  9. 运行 npx jest src/stores --ci --maxWorkers=2 与 npm run typecheck。
  10. 修改持久化 store 的写入顺序后,在指定设备的 Release 构建上杀掉并重启应用,验证屏幕显示内容仍在(iOS 用 xcrun simctl terminate/launch;Android 用 adb shell am force-stop/monkey)。
  11. 从仓库根运行两个检查脚本,修复所有 FAIL 行直至输出 RESULT: PASS。

适用场景

  • 在 React Native/Expo 应用中引入 Zustand 5 做客户端状态管理。
  • 需要“先写盘再显示”的一致性保证,避免后台被杀导致未保存数据丢失。
  • 从 Redux、Context、MobX 或 persist+AsyncStorage 迁移到 Zustand。
  • 需要可撤销会话(命令日志 + 快照恢复)。
  • 希望在 CI 中通过静态检查防止 store 规则漂移。

限制与注意事项

  • 资料未提供该技能的收费、价格或商业授权信息,暂未提供。
  • 规则严格绑定 React Native/Expo、Zustand 5.0.15、TanStack Query、src/core/result.ts 等前置结构;非该技术栈项目需要自行调整。
  • 检查脚本依赖模板与示例的目录结构,新增规则需同步更新 scripts/selftest.mjs 与测试夹具。
  • 资料未说明浏览器端或非 React Native 平台的运行效果,暂未提供。
  • SKIP src/stores/settings/settings-reducer-cases.ts 是预期跳过项,仅出现在尚未添加该用例文件的应用中。

反模式(资料列举)

模块级 create 单例、先 set 后写、忽略写入结果、选择器返回新对象、useTasksStore() 读取全部、跨 store 裸写只 reload 一个、useEffect 将 query.data 复制进 store、异步 store action 先 fetch 再 set、reducer 中使用 Date.now()/Math.random()/randomUUID()、事件式 action 名(task-added)、直接 push 状态数组、仅发布型 Premium store、撤销历史存盘、浮动安装(@latest 或 caret 范围)。