You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React中useMemo依赖数组引用类型的工作机制及疑惑解析

React useMemo 依赖对比疑问解析

代码示例

父组件

import React, { useState } from 'react';
import List from './List.component';
 
const Counter = () => {
  const [test, setTest] = useState(false);
  const [list, setList] = useState([3, 2, 1]);
 
  console.log('counter re-render');
 
  return (
    <>
      <button onClick={() => setTest(!test)}>123</button>
      <List items={list} />
    </>
  );
};
 
export default Counter;

子组件

import { useMemo } from 'react';
 
const List = ({ items }) => {
  const sortedItems = useMemo(
    function () {
      console.log('Sorting');
      return items.sort((a, b) => a - b);
    },
    [items]
  );
 
  console.log('List re-render');
 
  return (
    <ul>
      {sortedItems.map((item, ind) => (
        <li key={ind}>{item}</li>
      ))}
    </ul>
  );
};
 
export default List;

现象与疑问

初始渲染时,控制台输出:counter re-render、Sorting、List re-render;但点击按钮更新与子组件无关的test状态时,控制台仅输出counter re-render和List re-render,并未触发Sorting。

疑问点:

  • 为何此时不会触发Sorting?
  • useMemo的依赖数组底层是如何工作的?已知它使用Object.is进行比较,但每次渲染时list是新创建的数组引用,原本认为useMemo会在每次渲染时都执行,这是为什么?

问题解析

1. 点击按钮后未触发Sorting的核心原因

问题出在**Array.sort()是原地排序方法**上:
初始渲染时,items.sort()直接修改了父组件useState中存储的原始数组[3,2,1],将其变为[1,2,3]。而useState对于引用类型的状态,只有调用setList传入新引用时,状态才会更新。

当点击按钮更新test状态时,父组件重新渲染,但useState([3,2,1])不会重新创建新数组——useState的初始值仅在组件第一次挂载时生效,后续渲染会直接复用当前状态的引用。也就是说,此时父组件传给子组件的items,和上次子组件接收的items是同一个引用。

useMemo通过Object.is对比依赖数组中的items,发现前后引用完全一致,因此不会重新执行排序函数,也就不会输出Sorting。

2. useMemo依赖数组的底层逻辑

useMemo的工作逻辑可以拆解为:

  • 组件每次渲染时,会逐一对比依赖数组中每一项与上一次渲染时缓存的依赖值,对比规则采用Object.is(和===基本一致,仅在处理NaN、-0这类特殊值时有区别)。
  • 若所有依赖项与上一次完全一致,直接返回之前缓存的计算结果;只要有任意一项依赖发生变化,就会重新执行回调函数,更新缓存结果。

你之前的误解在于认为父组件每次渲染都会创建新的list数组,但实际上useState(initialValue)的初始化逻辑仅执行一次,后续渲染会跳过初始化,直接复用当前状态的引用。只有当调用setList传入新数组(比如setList([...list]))时,list的引用才会改变。

补充验证

如果想让点击按钮时触发Sorting,可以修改父组件,让每次渲染都传入新的数组引用:

// 方式1:用函数初始化,每次渲染都生成新数组
const [list, setList] = useState(() => [3, 2, 1]);
// 方式2:传递子组件时生成新数组
<List items={[...list]} />

这样每次父组件渲染,子组件接收的items都是新引用,useMemo就会重新执行排序函数,输出Sorting。

内容的提问来源于stack exchange,提问作者Beka Lobzhanidze

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 22:43:20