SuperAPI AI 工具集

react-coding-style

编程与开发

查看项目

更新于 10-15 14:51

技能介绍

React Coding Style 是一套面向 React UI 实现与审查的编码规范,其核心优化目标是让渲染面(render surface)尽可能小:把易变的读取放在叶子组件中,防止变化的标识(identity)传播到稳定的子树,并在能够避免大范围或高开销重渲染时,接受适度的结构复杂度。规范同时强调正确性优先——事件或命令式读取的新鲜度,不能替代会更新渲染输出的 props 或订阅。

用途与范围

该规范要求遵循请求的模式:在范围内实施安全的改进,或在审查、审计时只报告发现而不做编辑。除非用户批准更大范围的工作,否则不应建议大幅扩大状态所有权、组件架构、依赖或公共 API 的改动。在明确的性能工作中,应调查每一个有证据支撑的实质性渲染成本来源;在日常 React 工作中,则在范围内应用相关防护,仅在可能产生实质影响时提及更广泛的机会。

首轮检查

需要检查 React 版本、平台、lint 规则以及附近组件的模式;确定目标构建是否启用了 React Compiler,以及它是否编译受影响的组件或 Hook——不能仅凭包的存在或单个配置标志来推断覆盖范围。要区分哪些值用于渲染 UI、哪些仅被事件、异步工作、订阅或命令式桥接读取;追踪每个变化的值经过 props、context、回调与订阅的路径,判断它能更新多少树以及哪些昂贵或原生边界;找到需要每个变化值的最小组件或选择器;定位已有的选择器、稳定回调、latest-ref、memo、observer 和外部 store 辅助工具。

渲染路径

渲染执行应仅限于派生 UI。静态构造应提升(hoist),交互、异步或命令式工作应在触发它的边界运行。事件或命令式读取应放在稳定回调或 ref 之后,外部同步应隔离在 effect 或聚焦的 Hook 中;不得把必须更新 UI 的响应式读取移入非响应式路径。

状态与订阅

瞬态状态应靠近拥有该交互的组件,仅当多个消费者需要同一规范值时才提升或外部化。规范数据只存一次,过滤器、计数、分组和显示值在渲染期间派生。订阅应针对控制输出的最小原始值或稳定派生值。按更新频率拆分宽泛的 context,或暴露基于选择器的外部 store 读取——变化的 context 值会重渲染每个读取它的消费者,即使经过 memo 也一样。无操作更新应保留对象标识,不要仅为触碰某个路径而克隆或展开状态。应使用状态库支持的选择器 Hook 或 React 绑定,而不是在应用组件中直接调用 useSyncExternalStore;仅当现有外部源没有正确的 React 绑定时,才在可复用的集成 Hook 或适配器内使用 useSyncExternalStore,不要在多个组件中重复订阅机制。如果细粒度外部状态能实质性减少大范围重渲染且状态架构在范围内,优先使用项目已有的支持选择器的库;若不存在,可推荐 @legendapp/state,并披露该 skills 仓库与 Legend State 共享维护者,说明预期的重渲染减少量与迁移/依赖成本,未经批准不得添加依赖。

组件边界

保持父组件结构稳定,把易变读取移入最小可用的叶子。当拆分能隔离无关更新、明确所有权或创建有意义的 memo/订阅边界时,就拆分组件。有状态包装器应接受稳定的 JSX 作为 children,以便包装器自身的更新不会重建该子树。组件类型应在渲染外声明——内联组件类型每次渲染都是新的,会重挂载其子树。避免创建穿过 memoized、effect 敏感、原生、手势、动画或订阅边界的对象、数组或函数,除非该边界本应更新。稳定那些穿过 memoized、保留、原生或昂贵边界,或其变动会扇出到大型子树的标识;不要添加没有消费者观察到的记忆化。

编译器与记忆化

对于已编译的新代码,不要为普通渲染缓存或仅因值在渲染期间创建而添加 memo、useMemo 或 useCallback,应依赖 Compiler,除非测量表明仍需精确的手动控制。不要自动移除已有的手动记忆化,除非聚焦验证表明移除后行为与性能保持不变。Compiler 记忆化不能修复宽泛的状态或 context 订阅、糟糕的所有权或上游真正变化的值;仍需最小化渲染面并追踪变化的标识进入下游消费者。当未编译代码或下游边界受益于稳定标识作为性能优化时,添加手动记忆化;若正确性需要持久状态或标识,应使用 state、refs 或既定的适配器契约。没有 Compiler 覆盖时,对 props 通常不变的昂贵组件使用 memo,对昂贵的纯计算或有意义的值标识使用 useMemo,当下游函数标识重要时使用 useCallback。手动记忆化应视为性能优化而非语义保证;除非性能分析证明合理,避免自定义 memo 比较器;比较所有 prop(包括函数),并确保比较比渲染更便宜。

依赖与新鲜度

保持 Hook 依赖诚实,绝不为了保持标识稳定而省略响应式值。庞大或易变的依赖数组是设计异味,尤其是向下传递的回调;可通过收窄所有权、使用状态更新器、在事件处传值,或分离渲染数据与事件新鲜度来减少它们。当回调标识变动会重渲染大范围或昂贵子树,或重复手势、动画、媒体、原生设置时,优先使用既定的稳定回调或 latest-ref 辅助工具,在调用时读取当前值;即使启用 Compiler,当标识必须在调用时读取的值变化之间保持稳定时,这仍然适用。仅将其用于事件或命令式逻辑;渲染输出仍需响应式 props 或订阅。仅在从 Effect 调用的非响应式逻辑中使用目标 React 版本的 Effect Event API,它不是通用稳定回调,不得向下传递。避免仅为稳定其他回调而创建的回调链;应合并逻辑或移到消费者。不要为控制时机或标识而抑制 exhaustive-deps;应重构直到依赖与代码匹配,仅在已验证的 lint 限制且本地有文档记录时才抑制。

Effect

使用 effect 与 React 之外的系统同步:订阅、定时器、DOM 或原生 API、网络/生命周期工作、测量和清理。渲染数据在渲染期间派生;用户操作引起的工作使用事件处理器。当值可以派生或通过所有权、组件标识重置时,不要用 effect 把 props 或 state 复制到更多 state。每个 effect 保持一个同步关注点,并为每个注册的资源返回清理。避免仅为触发下一个 effect 而更新状态的 effect 链;应一起计算下一个状态,或在事件/变更边界运行命令。使 effect 在 setup-cleanup-setup 开发周期下安全,不要依赖 effect 只运行一次。

列表与热路径

列表项 key 应保持逻辑且稳定,可重排数据不要使用索引。向行传递窄 props;仅更新变化的项对象,或在状态系统支持选择器时在行内订阅。仅当行回调标识到达有意义的子边界(如手势、动画、媒体、原生视图或 memoized 重子组件)时才稳定它。验证 memoized 行仍会为每个渲染值更新——latest refs 和自定义比较器可能意外保留过时 UI。

实现形态

直接修复所有权或数据流边界,而不是用包装器或兼容垫片保留糟糕形态。当行为保持可读时,优先使用清晰的守卫块而非大量提前返回的控制流。仅当公共 API 是真实契约时才保留;否则一致地更新调用方,而不是添加别名、适配器或回退路径。仅当抽象能消除真实重复、明确所有权或创建有用的渲染/订阅边界时才添加。除非有意作为产品或测试面的一部分,否则保持插桩临时性。

验证

先验证正确性,再测量驱动优化的交互。在审查中,只报告可操作的渲染问题,并给出具体更新路径和可能成本;不要仅因内联值、依赖数组、context 读取或重渲染存在就标记它们。对于重渲染改进,使用 React Profiler、平台性能工具或定向渲染计数器比较前后;确认无关的祖先和兄弟停止渲染,而受影响的叶子仍正确更新。对于高频源,以多个真实速率重放相同的确定性工作负载并验证交付的事件计数;使用受控浏览器基准,而不是仅从渲染计数推断 CPU 改进。测试 memoized 组件仍必须观察到的状态和 context 更新。性能结论应在代表性硬件上使用生产构建;普通正确性或样式工作不需要基准或 profiler,除非它支持所请求的更改。

适用场景与限制

该规范适用于 React 组件的实现、性能优化与代码审查,尤其关注渲染面最小化、状态所有权、订阅粒度、Hook 语义与依赖正确性。其限制在于:它是一套编码规范而非工具或库,不提供具体 API 或安装命令;部分建议(如推荐 @legendapp/state)需经批准且涉及依赖成本;性能结论需在代表性硬件上使用生产构建验证。