glownight

返回

useEffect 依赖写错,最常见会出这几类 bug:

  1. 值“过期”(stale closure)
    effect 里拿到旧的 state/props,比如定时器里 count 永远是旧值。

  2. 不触发更新
    漏了依赖,参数变了但 effect 不重跑,请求/订阅不更新。

  3. 死循环或频繁重跑
    把每次 render 都新建的对象/函数放进依赖,导致反复请求、反复 setState。

  4. 资源泄漏
    重跑时没正确清理,出现重复订阅、重复定时器、内存泄漏。

  5. 异步竞态
    前一次慢请求后返回,覆盖后一次快请求结果(显示“旧数据”)。


避免闭包陷阱的实用做法:

  1. 开启并遵守 eslint-plugin-react-hooksexhaustive-deps
  2. effect 里用到的响应式值(props/state)都写进依赖。
  3. 需要“基于上次值更新”时,用函数式更新:setCount(c => c + 1)
  4. 不想因某值变化而重跑 effect,但又要读最新值时,用 useRef 保存最新值。
  5. 对对象/函数依赖做稳定化:useMemo / useCallback
  6. 一个 effect 只做一件事,拆分副作用,减少错误依赖。
  7. 异步请求加取消/忽略机制(AbortControllerignore 标记)并在 cleanup 里处理。
  8. 如果是 React 19,可用 useEffectEvent 读取最新值,减少闭包问题。

示例(典型闭包坑):

useEffect(() => {
  const id = setInterval(() => {
    setCount(count + 1); // count 是旧值
  }, 1000);
  return () => clearInterval(id);
}, []); // 漏依赖
tsx

改法:

useEffect(() => {
  const id = setInterval(() => {
    setCount(c => c + 1); // 永远基于最新值
  }, 1000);
  return () => clearInterval(id);
}, []);
tsx
useEffect 依赖项写错常见会引发哪些 bug?如何避免闭包陷阱?
作者 glownight
发布于 2025年4月9日