[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]);
问题:
let interval是 component scope 变量,React 不 detect mutation → cleanup useEffect 的[interval]deps 实际只触发 mount 时一次- StrictMode 双 mount 会触发 2 个 setInterval,但只 cleanup 一个 → 内存泄漏 + 多个 timer 同时跑
- 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 compile0 错(修了 3 处interval引用) - ✅
pnpm build7.29s - ✅
pnpm scan:react0 命中(用此 issue 验证扫描器准确性)
如何避免再犯¶
- useEffect 注册的副作用必须 return cleanup
- 跨函数共享句柄用 useRef,不要用 component scope let
- cleanup 函数与注册放同一个 useEffect 内
元价值:scan:react 第一个发现¶
本 issue 是 v0.10.46 新做的 scripts/scan-react-lifecycle.py 第一次跑出来的命中。
基础设施有用 — 自动发现了一处反模式。