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

React useState传入相同引用仍触发组件重渲染问题

问题核心原因

你碰到的现象完全是React的正常运行表现,useState本身没有识别异常,本质是两个认知误区:

  • 对useState跳过更新的规则记混了:只要调用setState传入的值和当前状态用Object.is比较相等,React就会直接跳过后续重渲染和effect执行,这个规则不是只在首次更新才生效
  • 错把正常的状态更新重渲染,当成了「传相同值触发的无效重渲染」

对应代码的完整执行流程

顺着你的代码走一遍运行时序,就能完全对上控制台打印的所有日志:

const arr = [
  { name: 'Khaled', id: 0 },
  { name: 'Mohamed', id: 1 },
  { name: 'Ahmed', id: 2 },
  { name: 'Mohsen', id: 3 },
];

function App() {
  console.log('num $1');

  const [same, setSame] = useState({});

  useEffect(() => {
    console.log('num $3');

    const test = arr.find((el) => el.id === 0);
    console.log(Object.is(same, test));
    setSame(test);
  }, [same]);

  console.log('num $2');

  return <div>welcome</div>;
}
  1. 首次挂载阶段
    组件第一次跑函数体,先打印num $1,初始化same状态为你传入的空对象{},接着打印num $2,完成首次DOM渲染把<div>welcome</div>挂到页面上。
    挂载完成后执行绑定的useEffect,打印num $3。这时候拿到的test是arr里id为0的那个对象的引用,和当前存的空对象状态做Object.is对比结果是false,控制台打出false,随后调用setSame(test)触发状态更新,React安排下一次重渲染。
  2. 第一次重渲染阶段
    组件第二次跑函数体,打印num $1。React对比新传入的test和上一轮的空对象,两个值引用不一样,正常把same更新为test,走完整个渲染流程,接着打印num $2提交DOM更新。
    更新提交完检查effect依赖,发现same的值从空对象变成了arr[0]的引用,依赖变了,所以再次执行effect回调,打印num $3。这时候再取test还是arr[0]的同一个引用,和当前的same做Object.is对比结果是true,控制台打出true。
    这时候再调用setSame(test),React检测到传入的值和当前状态完全相等,直接终止更新流程,不会再触发重渲染,也不会重复跑effect,日志到这里就停了。

关键点说明

你看到的两次num $1/num $2打印,一次是组件首次挂载的必然执行,一次是same从空对象改成arr[0]引用的正常更新渲染,根本不是传相同值触发的无效重渲染。最后一次传相同引用调用setSame的时候,React已经正确跳过了更新,没有产生多余渲染。

控制台实际运行日志:
控制台运行日志截图


内容的提问来源于stack exchange,提问作者Muhammad Hossam

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 19:36:44