React 禁止 useEffect
直接的 useEffect 是一个与外部系统同步的逃生舱(escape hatch)。它应被限制在经审查、面向领域的 hook 中,用于真正的外部系统;组件应保持无 effect 状态。
范围
Effect 替换会改变受影响的行为。仓库级别的 lint 执行是独立的任务:仅在收到请求或现有仓库策略要求时应用。审查请求产生发现结果(findings);实施请求授权范围内的替换和验证,无需额外审批节点。
工作流
-
在受影响的代码范围内查找直接导入、
React.useEffect(...)命名空间调用以及useEffect的包装器。 -
对每个 effect 进行分类并按以下优先顺序替换:
- 渲染时计算
- 服务器、加载器或框架数据 API
- 仓库的服务器状态层(例如,使用 TanStack Query 的
queryOptions工厂,其中变量作为 key,signal传递) - 事件处理器、action 或 mutation
- 基于 key 的组件边界
useSyncExternalStore- 面向真实外部系统的经审查的领域特定 hook
如果都不适用,则在保留 effect 之前明确说明外部同步契约。替换方案 涵盖了非显而易见的案例。
-
保留仓库的框架、数据层、lint 风格和依赖策略。仅在请求或仓库策略授权时添加库;如果没有服务器状态层,将剩余的 effect 视为设计缺口。
-
运行 lint、类型和测试门控(gates),以及针对更改行为的最小真实 UI 或 hook 检查。
合法的 Effect
将所有合法的 effect 放入一个以其外部系统命名的 hook 中(例如聊天连接、地图 SDK、小部件),并放在其自己的文件中。该 hook 负责设置和清理,将其响应式输入作为参数,并列出设置或清理读取的每个值,以便 react-hooks/exhaustive-deps 通过且无需抑制。优先使用仓库中已有的 hook。
hook 必须专门针对一个外部系统。一个接受回调函数和调用者提供的依赖数组的通用包装器(如 useUpdateEffect(fn, deps)、useMountEffect(fn))是换了个名字的直接 effect;服务器状态、prop 副本、用户操作和基于 key 的重置各自有其上述的替换方案。
强制执行
当强制执行在范围内时,扩展现有 lint 规则,使其拒绝命名的 useEffect 导入和 React.useEffect(...) 调用。只允许命名的、经过审查的集成 hook 文件,而不是 src/hooks/** 这样的目录;将通用包装器及其调用者报告为违规项,以便迁移。在规范的本地门控和 CI 中运行相同的 lint 范围,并使用固定版本的工具。
在审查中,每个新的直接 effect 都是一个发现结果(finding),除非该更改记录并证明了合法的外部系统边界。要求进行替换,而不是调整依赖数组。
边界
- 将迁移范围限制在受影响或请求的代码范围内。
- 除非请求,否则保留
useLayoutEffect、useInsertionEffect、框架生命周期 API 以及非 React effect 系统(包括 lint 规则)。 - 仅对测量的行为或已建立的仓库模式应用性能原语。
- 上游归因:upstream。