glownight

返回

一句话核心结论#

React 对依赖数组里的每一项,都做 「全等比较(===)」,只要有任意一项变了,effect 就重新执行。


1. 底层判断规则#

每次组件渲染,React 都会做这 3 步:

  1. 记录上一次的依赖数组 [a, b, c]
  2. 拿到本次新的依赖数组 [a, b, c]
  3. 逐项进行 === 比较
    • 全部相等 → 不执行 effect
    • 任意一个不等 → 执行 effect

关键:是「全等比较」,不是「值相等」#

// 基本类型:值相同 → === 相等 → 判定不变
1 === 1true
'abc' === 'abc'true

// 引用类型:地址相同 → === 相等
// 地址不同 → 即使内容一样,也判定变化!
{} === {} → false
[] === [] → false
()=>{} === ()=>{} → false
js

2. 不同数据类型的判断结果#

① 基本类型(数字、字符串、布尔、null、undefined)#

直接比较值,值不变 → 判定不变

// 只会在 count 真正变化时执行
useEffect(() => {
  console.log(count);
}, [count]); 
js

② 引用类型(对象、数组、函数)#

比较内存地址地址变了 → 判定变化 这是 90% 的 bug 来源!

// 每次渲染都创建新对象 → 地址变 → effect 每次都执行!
useEffect(() => {
  console.log('我每次渲染都跑!');
}, [{ name: 'react' }]); 

// 每次渲染都创建新数组 → effect 每次执行
useEffect(() => {
  console.log('我每次渲染都跑!');
}, [[1,2,3]]); 
js

3. 最容易踩坑的 3 个场景#

坑 1:直接在依赖里写对象/数组#

function App() {
  const [count, setCount] = useState(0);

  // ❌ 错误:每次渲染都是新对象,effect 无限执行
  useEffect(() => {
    console.log('执行');
  }, [{ id: 1 }]);
}
js

解决方案:用 useMemo 缓存对象/数组

const obj = useMemo(() => ({ id: 1 }), []);
useEffect(() => {
  console.log('执行');
}, [obj]); // ✅ 地址不变
js

坑 2:依赖里写函数(每次渲染都是新函数)#

// ❌ 错误
useEffect(() => {
  fetchData();
}, [fetchData]); 
js

解决方案:用 useCallback 缓存函数

const fetchData = useCallback(() => { ... }, []);
useEffect(() => {
  fetchData();
}, [fetchData]); // ✅ 地址不变
js

坑 3:空依赖数组 []#

所有项都不变 → 只在组件挂载执行一次,卸载清理一次


4. 完整执行流程图#

  1. 组件初次渲染 → 执行 effect
  2. 组件更新 → 生成新依赖数组
  3. React 逐项 === 对比新旧依赖
    • 全相等 → 不执行
    • 有一个不等 → 先执行清理函数 → 再执行新 effect

总结#

  1. useEffect 依赖判断用 === 全等比较
  2. 基本类型比数值引用类型比内存地址
  3. 对象/数组/函数必须用 useMemo / useCallback 缓存,否则会重复执行
  4. 空依赖 [] = 只执行一次
useEffect 依赖数组判断机制
作者 glownight
发布于 2025年11月28日