创见博客
useEffect中的依赖项的比较Object.is
七崽爱吃小饼干2026/01/06阅读 0专栏 React

一、依赖项的比较是浅比较

深比较的性能消耗特别大,深比较需要递归遍历对象 / 数组的每一层属性,直到所有基本类型值都对比完成。

而浅比较(Object.is)的成本是 O (1):仅对比引用或基本类型值,几乎无性能损耗,能保证渲染流程的高效性。

二、核心原理:Object.is 与依赖项比较

1. Object.is 的比较规则

Object.is 是 ES6 新增的等值判断方法,规则介于 === 和普通相等之间:

  • 和 === 一致:严格区分类型(比如 1 !== '1',Object.is(1, '1') // false);
  • 和 === 不同:修复了两个特殊场景:
    • Object.is(NaN, NaN) // true(而 NaN === NaN // false);
    • Object.is(+0, -0) // false(而 +0 === -0 // true)。

2. React 如何用 Object.is 比较依赖项

当组件重新渲染时,React 会:

  1. 保存上一次渲染时依赖数组的“快照”(比如 [a, b]);
  2. 用 Object.is 逐个对比新/旧依赖项:
    • 若所有依赖项都满足 Object.is(新值, 旧值) === true → 不执行 useEffect 回调;
    • 若任意一个依赖项不满足 → 执行清理函数(若有),再执行新的 useEffect 回调。

三、常见误区:引用类型的依赖比较

需要特别注意:Object.is 对引用类型(对象、数组、函数)的比较是引用比较(和 === 一致),而非深比较:

jsx
import { useEffect, useState } from 'react';

function Demo() {
  const [obj, setObj] = useState({ name: 'test' });

  // 每次渲染都会创建新对象,Object.is(新对象, 旧对象) → false,每次都执行
  useEffect(() => {
    console.log('obj 变化');
  }, [obj]);

  return (
    <button onClick={() => setObj({ name: 'test' })}>
      赋值相同内容的新对象
    </button>
  );
}

问题:

即使对象内容完全相同,只要是新创建的引用,Object.is 就会判定为“变化”,导致 useEffect 每次渲染都执行。

解决:

  • 对对象/数组:使用 useMemo 缓存,只在内容变化时更新引用;
  • 对函数:使用 useCallback 缓存,只在依赖变化时更新引用。

四、和 === 的核心区别(React 选择 Object.is 的原因)

React 选择 Object.is 而非 ===,核心是为了处理 NaN 场景:

  • 若用 ===:依赖项为 NaN 时,每次渲染都会判定为“变化”(因为 NaN === NaN // false),导致 useEffect 无意义地重复执行;
  • 用 Object.is:NaN 被判定为“未变化”,符合开发者的预期。

总结

  1. useEffect 的依赖项比较使用 Object.is 算法,而非普通的 ===;
  2. Object.is 相比 ===,修复了 NaN 和 +0/-0 的判断逻辑,更符合 React 依赖比较的需求;
  3. 对引用类型,Object.is 仍为引用比较,需用 useMemo/useCallback 缓存避免不必要的重执行。
评论
0/100