用途
react-ban-use-effect 用于审查或替换 React 中的 useEffect 调用,或在仓库中强制执行“禁止直接使用 effect”的策略。它把直接 useEffect 定位为与外部系统同步的逃生舱:组件不应调用它,只有经过审查、针对真实外部系统的领域专用 Hook 才可以。
主要能力
- 在改动范围内查找直接 import、React.useEffect(...) 命名空间调用,以及围绕 useEffect 的包装器。
- 对每个 effect 进行分类,并按以下优先级顺序替换:
- 渲染期计算
- 服务端、loader 或框架数据 API
- 仓库的服务端状态层(例如 TanStack Query 的 queryOptions 工厂,变量放在 key 中并透传 signal)
- 事件处理器、action 或 mutation
- 带 key 的组件边界
- useSyncExternalStore
- 经过审查、针对真实外部系统的领域专用 Hook
- 如果以上都不适用,需在保留 effect 前说明其外部同步契约。
- 在范围内时,扩展仓库现有 linter,使其同时拒绝命名 useEffect import 和 React.useEffect(...) 调用。
使用方式
- 审查请求产生发现项;实现请求授权进行范围内的替换与验证,无需额外的审批检查点。
- 保留仓库的框架、数据层、lint 形态和依赖策略。只有在请求或仓库策略授权时才添加库;若没有服务端状态层,应将剩余 effect 称为设计缺口。
- 运行 lint、类型和测试门禁,并对改动行为做最小的真实 UI 或 Hook 检查。
- 强制执行时,只允许命名且经过审查的集成 Hook 文件,绝不允许 src/hooks/** 这类目录,也绝不允许通用包装器;将包装器及其调用者报告为需迁移的违规项。保持本地门禁与 CI 使用同一 lint 面;agent 指引或可选扫描器不能替代它。不要使用 @latest 诊断工具作为持久强制执行手段。
合法 Effect 的写法
每个合法 effect 都应放在以外部系统命名的 Hook 中(如聊天连接、地图 SDK、组件),并单独成文件。该 Hook 负责 setup 和 cleanup,将响应式输入作为参数,并列出 setup 或 cleanup 读取的每个值。调用它的组件保持无 effect。优先使用仓库已有的 Hook。
禁止:
- 编写接收回调和调用方提供依赖数组的通用包装器(useUpdateEffect(fn, deps)、useAsyncEffect(fn, deps)、useMountEffect(fn));
- 抑制 react-hooks/exhaustive-deps;
- 使用例外 Hook 来获取服务端状态、把 props 复制进 state、转发用户操作,或隐藏本应放在带 key 边界处的重置。
适用场景
- effect 实现与重构。
- 仓库级 lint 策略的制定与执行。
- 代码审查中识别新增的直接 effect。
限制与边界
- 将迁移范围限制在改动或请求的范围内。
- 除非被要求,否则不要改动 useLayoutEffect、useInsertionEffect、框架生命周期 API 以及非 React 的 effect 系统,包括在 lint 规则中。
- 仅在行为经过测量或仓库已有既定模式时,才应用性能原语。
- 在审查中,每个新增的直接 effect 都是发现项,除非该改动记录并证明了合法的外部系统边界。应要求替换,而不是调整依赖数组。