症状
本篇指南处理“React useEffect 闭包过期 — 缺少依赖”。请从症状出发,按顺序执行诊断,在推进前确认每个假设;随后通过修复与验证步骤闭环。
可观察到的症状包括:
- effect 一直使用属性或状态的初始值
- 定时器每次 tick 打印相同的计数
- lint 规则 react-hooks/exhaustive-deps 警告缺少依赖
诊断
先在隔离环境中复现故障,再按顺序执行以下检查,逐步缩小范围。
步骤 1
npx eslint src --rule '{"react-hooks/exhaustive-deps": "warn"}'
该规则会确切指出数组中缺失哪个依赖。
步骤 2
git diff src/hooks/useTicker.js
每次运行记录捕获值,可看出是初始值还是最新值。
常见诱因(按出现频率排序):
- 依赖数组遗漏了 effect 内读取的值
- 状态之后会变化却使用空依赖数组
- 回调捕获了旧值且未重建
根因
每次渲染都会创建新闭包;effect 捕获创建它那次渲染的值。若所用值不在依赖数组中,effect 会保留旧闭包并读取过期值。
修复
把 effect 内使用的每个响应式值列入依赖,或把最新值放入 ref 并在内部读取 ref.current;不得无理由关闭 exhaustive-deps。
注意:加入每次渲染都变化的依赖会使 effect 循环;应用 useCallback 或 useMemo 稳定该值。
function Ticker() {
const [count, setCount] = useState(0);
useEffect(() => {
const id = setInterval(() => {
setCount((c) => c + 1); // functional update avoids stale count
}, 1000);
return () => clearInterval(id);
}, []); // safe: no external value read
return <span>{count}</span>;
}
// reading latest state without re-running
const latest = useRef(count);
useEffect(() => { latest.current = count; }, [count]);
验证
运行该场景并确认 effect 读到最新值;lint 规则应不再报告缺失依赖。
修复后补充一条回归测试,复现原始症状,防止问题再次出现。
这类故障通常是环境问题而非纯逻辑错误,因此在缺陷记录中应写明完整命令、环境变量与工作目录。请用最小的脚本复现,待最小用例清楚后再扩大范围。为这类故障准备一份简短的排查清单,能显著减少下次排障时间。