关于 legend-state-best-practices
面向使用 Legend State 的开发者,提供 React 状态所有权、订阅边界、追踪语义及持久化实践建议。该技能侧重代码审查与决策规范,并非 API 参考。
主要功能
- 01选择状态所有权边界
- 02缩小 React 订阅范围
- 03区分追踪与非追踪读取
- 04规划响应式副作用
- 05检查持久化与列表性能
适用场景
- 01设计新的响应式状态结构
- 02审查组件订阅和渲染更新
- 03修复状态镜像或副作用问题
- 04验证列表和持久化改动
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ legendapp/ legend-skills --skill legend-state-best-practices安装后在 Claude Code 中输入/legend-state-best-practices确认已加载。
附带文件agents/openai.yamlreferences/persistence-and-sync.md
详细介绍
Legend State 最佳实践
使用 Legend State 将响应式状态的所有权划分到尽可能小且实用的边界。
验证接口
修改代码前:
- 检查已安装的
@legendapp/state版本、导入、附近的 store 辅助函数、持久化设置和测试。 - 查阅当前文档,确认准确的签名和特定版本的迁移指南:
- 搜索现有的 store 工厂、类型化字段钩子、响应式组件和命名约定,再添加新的抽象。
- 分别识别必须更新已渲染 UI 的值,以及仅由命令、回调、原生桥接或异步任务读取的值。
本指南是规范,不是 API 参考。若文档与代码不一致,请优先参考已安装版本的源码和类型定义。
选择模式
- 构建: 编写代码前,确定 observable 的所有权、渲染订阅、effect、持久化或同步边界,以及验证方式。
- 审计: 检查所有权、订阅范围、非响应式读取、React 与 observable 之间的镜像、effect、持久化和热点路径;仅报告具体发现,并按影响和置信度排序。
- 修复: 检查相关的所有权和订阅路径,然后在对话已授权的范围内实现并验证。只有遇到尚未解决的产品决策、范围显著扩大或未经授权的外部操作时,才提出询问。
审计时,应追踪每项发现,从状态变更来源,经由被追踪的读取,一直到更新的组件或 effect。若不明确指出会唤醒哪些订阅者,以及这些订阅者会使用哪个渲染值,就不要将 observable 读取判定为范围过广或开销过大。
所有权
- 对于具有许多细粒度消费者的共享 UI、会话、设置、文档、选择和命令状态,使用 observables。
- 当嵌套 UI 需要字段级响应性,或处理程序需要稳定的 observable 句柄时,使用
useObservable管理组件生命周期内的状态。 - 若一次性局部 UI 状态没有更广泛的所有权需求、命令式读取需求或渲染扇出需求,则继续使用 React。
- 只存储一次规范数据。使用 selector、computed observable 或局部渲染计算派生显示值。
- 重用现有的 observable 引用。不要将 observable 传入
observable或useObservable,也不要仅为所有权、稳定性或 context 而将其放入另一个 observable。只有在有意创建一个链接,且其读取和写入会转发到源 observable 时,才将其嵌套其中。 - 保留单一规范所有者;不要在 React 状态与 observable 状态之间创建镜像。如果下游代码需要 observable 状态,请替换源头的 React 状态。仅在原生 API、存储或测量等真正的外部集成处进行桥接。
- 路由、文件选择、原生调用和其他应用专属工作应放在通用 observable store 之外。
通过 React context 共享 observable 时,应提供稳定的 observable 引用,并且仅在渲染其字段的最小范围消费者中调用 useValue。
React 订阅
在需要该值的最小组件或元素处订阅。
- 在 v3 中,使用
useValue;use$和useSelector仍是已弃用的别名,因此迁移修改过的用法时请改用useValue。 - 渲染可观察对象的原始值时,使用
useValue(value$)。对于派生 UI,使用useValue(() => ...),并返回控制渲染的基本类型或稳定结果。对于行选择,优先使用useValue(() => selectedId$.get() === id),而不是让每一行都订阅selectedId$再进行比较。 - 保留现有的
observer包装,但默认不要新增。如果测得大量useValue调用带来的钩子开销使observer更合适,请提出建议并说明取舍。实际读取仍应放在useValue中,因为当前 v3 指南不鼓励在渲染时直接调用.get()进行跟踪。 - 使用
Memo创建独立的响应式片段,使其不会随父级重新渲染;当片段还依赖父级渲染值时,使用Computed。 - 使用
Show或Switch实现响应式控制流,使用For处理可观察数组、对象或映射。只有在确认其节点复用行为适用于该行后,才使用For optimized。 - 当某个属性可以独立更新,且父组件开销较大时,使用响应式 DOM 或原生组件。不要让所有内容都响应式化;每增加一个边界,就会增加一个组件和一个订阅。
除非身份变化本身就是预期信号,否则避免从选择器返回新建的宽泛对象。
跟踪语义
- 在
useValue、observer、observe或其他跟踪上下文中,get()会订阅所读取的节点。在跟踪上下文之外,它只读取值。 peek()永远不会进行跟踪。对于需要获取当前值但不订阅的命令、事件处理程序、异步工作和命令式集成,请使用它。- 使用
get(true)进行浅层跟踪。Object.keys、Object.values、Object.entries、可观察数组的长度和循环方法,以及For,也会跟踪集合成员或结构,但不会跟踪子字段的变化。 - 访问可观察属性本身不会创建订阅;只有进行跟踪式读取时才会创建订阅。
- 在多个修改操作发生在尚未批处理的路径之外,且共同构成一次逻辑更新时,请使用
batch。
副作用与最新读取
- 对于需要在可观察对象变化时触发外部命令、缓存失效、持久化操作、测量重置或原生更新的持续响应,请使用
observe、useObserve或useObserveEffect。 - 对于一次性条件或就绪门控,请使用
when或whenReady;它们会解析或运行一次,然后停止观察。 - 仅当在渲染时执行是正确的情况下才使用
useObserve;如果工作必须在挂载后运行,则使用useObserveEffect。 - 保持副作用范围精简,并明确清理逻辑。顺序很重要时,应观察发生变化的源。
- 当外部监听器需要稳定的身份和最新的可观察值时,使用最新值 ref 或稳定回调。
不要使用副作用来同步本地所有者,也不要分发可以在修改发生处执行的工作。
持久化与同步
有关持久化或同步方面的更改,请阅读 persistence-and-sync.md。
列表与热路径
- 在行中,订阅渲染所需的最小原始值或稳定的派生值。共享主题值可能会正确更新所有受影响的行;当每行只用到主题或设置对象中的一个字段时,应避免让每行都订阅整个对象。如果行的其余部分开销较大,则将订阅移到最小的主题化叶节点中。
- 优先使用行级可观察值、选择器式布尔值、条目级订阅,或显式的列表失效信号,其影响范围应与 UI 相匹配。
- 只有当现实的最大规模和行成本允许挂载所有条目时,才对非虚拟化集合使用
For。否则应建议使用虚拟化。 - 优先使用项目现有的虚拟化列表库。如果没有,则推荐
@legendapp/list中的LegendList,说明该技能仓库与 Legend List 由相同的维护者维护,解释依赖项和迁移成本,并且未经批准不要添加。保留所选列表的标识、测量和失效契约。
验证
- 测试选择器或响应式边界发生变化时,是否只有预期的组件会更新。
- 核心跟踪语义发生变化时,测试跟踪式、非跟踪式和浅层读取。
- 持久化或同步发生变化时,测试默认值、规范化、待处理变更、重试和元数据行为。
- 公共包发生变化时,运行类型检查和导出接口测试。
- 使用分析器或渲染计数证据支撑性能声明;组件看起来更小,并不能证明渲染次数更少。
评论
支持 Markdown