跳转至

[ISSUE-0030] use-countdown 双 useEffect 反模式

发现者:v0.10.46 新做的 pnpm scan:react 工具第一次跑出来的命中 相关源码src/hooks/use-countdown.ts:72-86(修前)

反模式

// 修前 ❌
let interval: number | undefined;  // component scope let

useEffect(() => {
  interval = window.setInterval(() => { setNewTime(initDate); }, 1000);
}, []);

useEffect(() => () => clearInterval(interval), [interval]);

问题

  1. let interval 是 component scope 变量,React 不 detect mutation → cleanup useEffect 的 [interval] deps 实际只触发 mount 时一次
  2. StrictMode 双 mount 会触发 2 个 setInterval,但只 cleanup 一个 → 内存泄漏 + 多个 timer 同时跑
  3. unmount race 时 cleanup 拿到的 interval 可能是旧值

修复

// 修后 ✅
const intervalRef = useRef<number | undefined>(undefined);

useEffect(() => {
  intervalRef.current = window.setInterval(() => { setNewTime(initDate); }, 1000);
  return () => {
    if (intervalRef.current) clearInterval(intervalRef.current);
  };
}, []);

setNewTime 内 / setNewDate 外部 API 也改成走 intervalRef.current

验证

  • pnpm compile 0 错(修了 3 处 interval 引用)
  • pnpm build 7.29s
  • pnpm scan:react 0 命中(用此 issue 验证扫描器准确性)

如何避免再犯

  • useEffect 注册的副作用必须 return cleanup
  • 跨函数共享句柄用 useRef,不要用 component scope let
  • cleanup 函数与注册放同一个 useEffect 内

元价值:scan:react 第一个发现

本 issue 是 v0.10.46 新做的 scripts/scan-react-lifecycle.py 第一次跑出来的命中。 基础设施有用 — 自动发现了一处反模式。

详见 React前端lifecycle清单