前端进阶之旅前端进阶之旅
  • 基础篇HTML/CSS/JS 打底
  • 进阶篇原理与工程化
  • 高频篇面试最常问的那批
  • 精选篇按模块收敛的总结
  • 手写篇常考代码手写实现
  • 面经篇真实面试问题复盘
  • AI 篇NEWAI 时代的前端考点
  • 历年面经NEW按年份追踪真实考点
  • 每日一题每天一道,攒手感
  • 专项自测100 题快速查漏
  • 小程序题库小程序专项刷题
  • 算法题库NEW在线编码即时判题
  • 知识卡片NEW碎片时间过考点
  • 面试题大全常见问题解析
  • AI 答疑NEW随时提问,即时解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • AI 定制路线NEW按你的简历现排
  • AI 知识地图NEW串起全站知识点
  • 原理篇React / Vue 源码拆解
  • HTTP从报文一路讲到 HTTPS
  • 浏览器渲染、事件循环、进程
  • 计算机基础Linux、网络、操作系统
  • 设计模式23 种模式怎么用
  • Node学习指南从环境搭建到服务端
  • NPM工作流script、依赖与发布
  • Docker容器化部署上手
  • Canvas图形与动画实战
  • 前端系统进阶学习大型项目工程化
  • 前端综合文章长期沉淀的实践文
  • 思维导图知识点全景图
  • 学习路线按图索骥不跑偏
  • AI 热点NEWAI 每日动态
  • 公众号动态公众号历史文章
  • 博客动态站长的技术博客
  • 开发者导航常用工具与文档站
  • 基础篇HTML/CSS/JS 打底
  • 进阶篇原理与工程化
  • 高频篇面试最常问的那批
  • 精选篇按模块收敛的总结
  • 手写篇常考代码手写实现
  • 面经篇真实面试问题复盘
  • AI 篇NEWAI 时代的前端考点
  • 历年面经NEW按年份追踪真实考点
  • 每日一题每天一道,攒手感
  • 专项自测100 题快速查漏
  • 小程序题库小程序专项刷题
  • 算法题库NEW在线编码即时判题
  • 知识卡片NEW碎片时间过考点
  • 面试题大全常见问题解析
  • AI 答疑NEW随时提问,即时解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • AI 定制路线NEW按你的简历现排
  • AI 知识地图NEW串起全站知识点
  • 原理篇React / Vue 源码拆解
  • HTTP从报文一路讲到 HTTPS
  • 浏览器渲染、事件循环、进程
  • 计算机基础Linux、网络、操作系统
  • 设计模式23 种模式怎么用
  • Node学习指南从环境搭建到服务端
  • NPM工作流script、依赖与发布
  • Docker容器化部署上手
  • Canvas图形与动画实战
  • 前端系统进阶学习大型项目工程化
  • 前端综合文章长期沉淀的实践文
  • 思维导图知识点全景图
  • 学习路线按图索骥不跑偏
  • AI 热点NEWAI 每日动态
  • 公众号动态公众号历史文章
  • 博客动态站长的技术博客
  • 开发者导航常用工具与文档站
首页程序员面试题库React setState 相同值 Object.is 跳过更新
ReReact状态与渲染

React state 设为相同值会跳过什么,Object.is 有哪些容易误判的情况?

相同值判断针对状态身份,不是深比较,也不是对组件函数调用次数的绝对承诺。

前端进阶之旅 · 一题精讲更新于 2026.09.06
React#状态与渲染
先看核心答案读代码示例
理解线索

分别判断值、身份和可见结果

  1. 基础值注意 NaN 与正负零的比较规则
  2. 对象身份同一引用与内容相等不是一回事
  3. 渲染结果不要用函数是否调用代替界面行为验证

正负零示例只是为了显式观察比较规则,普通业务应按自身数值语义决定是否归一化。

核心回答

先记住这个答案

useState 把下一状态与当前状态按 Object.is 比较,相同值通常使 React 跳过由这次状态更新带来的组件及子树工作。某些情况下 React 仍可能先调用组件再忽略结果,因此不能把“相同值”解释为函数绝不会执行。对象比较关注引用,同一对象改字段后再传回去仍可能被判为相同;两个内容一样的新对象则不同。Object.is 把 NaN 与自身视为相同,但区分零与负零,这也解释了它与普通严格相等比较的少数差别。

  • useState 的相等判断使用 Object.is
  • 同一对象引用不会因内部修改自动变成新状态
  • 跳过更新不能作为渲染函数副作用的保护

为什么数值看起来都是零却可能更新

JavaScript 的零和负零直接转成文字通常都显示零,但 Object.is 能区分它们。下面用显式格式化显示负零,避免已经发生状态变化却因为文本一样误以为 React 没有更新。再次设置同一个 NaN 则属于相同状态。

验收这类行为应检查界面和状态含义,而不是给渲染函数加计数副作用。即使某次相同值设置没有引发可见更新,之后父组件、Context 或其他 state 的变化仍可能让这个组件再次参与渲染。

让正负零的差异能够被看见TSX
import { useState } from 'react';

export default function SameValue() {
  const [value, setValue] = useState(0);
  const label = Object.is(value, -0) ? '-0' : String(value);
  return <>
    <button onClick={() => setValue(0)}>设为零</button>
    <button onClick={() => setValue(-0)}>设为负零</button>
    <button onClick={() => setValue(NaN)}>设为 NaN</button>
    <output>数值:{label}</output>
  </>;
}

初始显示零,设置负零后显式显示 -0;设置 NaN 后显示 NaN,再次设置 NaN 保持相同状态。这里不承诺每一步组件函数被调用的精确次数。

对象原地修改会让相同引用判断掩盖问题

如果先修改 user.name,再调用 setUser(user),React 看到的还是同一个对象引用。这会使界面更新行为和你已经改动的内存内容不一致;后续其他更新又可能让修改突然出现在屏幕上,增加排查难度。

正确方式是返回新的变更路径对象,保护旧版本。反过来,每次都构造内容相同的新对象也不会被 useState 深比较后自动跳过;若频繁产生无意义更新,可以在业务层判断确实没有变化时直接返回前一状态。

相同值跳过不能替代纯渲染与订阅设计

组件函数必须在可能再次执行的前提下保持纯度,不能因为某条路径大概率跳过就把请求、计数或资源创建放进渲染。父层和 Context 都有独立的更新来源,某个 setter 的相等优化并不封住其他入口。

对外部可变对象,仅把它放进 state 也不会让 React 订阅内部变化。应建立明确的通知与快照契约,再让界面消费稳定的结果。不要用不断提交一个新包装对象强行刷新来长期掩盖缺失的外部状态订阅。

回答前,多想一步

容易答错的地方

Object.is 会递归比较对象里的所有字段
它比较对象身份,不进行深层字段遍历;同引用的内部修改和不同引用的相同内容会得到不同于深比较的结果。
相同值时组件绝不执行,所以 render 副作用安全
React 不承诺组件函数在所有相同值场景都完全不被调用,其他更新也可能触发渲染;渲染副作用不能建立在跳过优化之上。
试着用自己的话回答

面试官还会怎么问?

返回 previous 能否表达这次操作没有变化?

可以,当业务确认状态无需改变时返回同一引用能保留身份;前提是没有先原地修改 previous,否则你已经破坏了历史内容。

为什么 setState 一个新空对象仍可能更新?

新空对象有不同身份,Object.is 不会因为字段都为空就认定相同。若业务没有变化应避免无意义的新对象,而不是期待状态系统做深比较。

组件用了 memo,还需要关注这个判断吗?

需要,memo 主要处理父层传来的 props 优化,自身 state 有独立更新语义。两者比较位置不同,不能用 memo 代替正确的状态建模。

从一道题,走向一组知识

把知识连起来

状态与渲染

React 嵌套对象怎样不可变更新,为什么只写展开运算符还会共享旧引用?

用不可变更新正确表达对象状态的身份变化

状态与渲染

React state 为什么是渲染快照,setState 后的回调为什么仍读到旧值?

理解旧对象被修改为何会影响历史快照

参考资料

  • React:useState identical values
  • MDN:Object.is

示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。

本题目录
  1. 先记住这个答案
  2. 为什么数值看起来都是零却可能更新
  3. 对象原地修改会让相同引用判断掩盖问题
  4. 相同值跳过不能替代纯渲染与订阅设计
  5. 容易答错的地方
  6. 面试官还会怎么问
  7. 把知识连起来
读懂,再试着讲出来

先看核心答案,再读代码。最后展开追问,检查自己有没有遗漏边界。

试着回答追问
浏览全部面试题理解原理,也关注真实的使用场景。回到顶部 ↑