CodeWalk

useEffect cleanup函数与依赖比较机制

作者:Yahuda · 2026-05-30 12:55

详细解释React useEffect的清理函数(cleanup function)执行时机和作用,以及React如何比较依赖数组(deps)来决定是否重新执行effect。请举例说明Object.is比较方式导致的潜在问题。

回答

Yahuda

Effect执行与清理时机

useEffect(() => {
  // effect逻辑
  return () => {
    // cleanup函数
  };
}, [deps]);

执行顺序:

  1. 组件挂载 → effect执行
  2. deps变化 → 先执行上一个effect的cleanup,再执行新effect
  3. 组件卸载 → 执行最后一个cleanup

cleanup函数作用:

  • 取消订阅(取消API请求、清除定时器)
  • 移除事件监听
  • 重置状态
  • 避免内存泄漏和多次effect重叠执行

依赖比较机制

React使用Object.is比较前后两次deps数组中的每个元素:

const prevDeps = [objA, num];
const nextDeps = [objA, num];
Object.is(prevDeps[0], nextDeps[0]) && Object.is(prevDeps[1], nextDeps[1]);

Object.is(类似===但不同点):

  • Object.is(NaN, NaN)true(而NaN !== NaN)
  • Object.is(+0, -0)false(而+0 === -0)

潜在问题:

  1. 引用类型每次渲染新创建

    useEffect(() => {}, [{a: 1}]); // 每次渲染都变!
    

    对象字面量每次都是新引用 → 死循环

  2. 函数作为依赖

    const fn = () => {};
    useEffect(() => fn(), [fn]); // 每次重新创建
    

    需要用useCallback包裹

  3. 数组/对象内联 → 永远触发effect

解决方案:

  • 使用useMemo/useCallback稳定引用
  • 使用useRef存引用类型
  • 比较原始值而非对象
  • 使用JSON.stringify或第三方深度比较(不推荐)