React 中 props 身份与重渲染

React 中对象、数组、函数等 props 引用变化如何影响子组件重渲染和 memo 生效。

#tech / dev / frame #resource / react #type / synthesis #status / growing

[!info] related notes

React 中 props 身份与重渲染

React 性能问题里一个非常高频的坑,是“值看起来没变,但引用其实变了”。

一句话定义

当对象、数组、函数作为 props 传递时,即使内容看起来一样,只要引用是新的,子组件和 React.memo 就可能把它当成变化。

为什么这件事重要

  • 父组件每次执行,常常都会创建新的对象和函数
  • 子组件如果依赖引用稳定性,就可能无意义重渲染
  • React.memo 是否真正生效,常常就卡在这里

底层直觉其实来自 JavaScript 的值语义

  • 原始值更接近“按值比较”
  • 对象、数组、函数更接近“按身份比较”

所以:

Object.is(1, 1) // true
Object.is('a', 'a') // true
Object.is({}, {}) // false
Object.is([], []) // false

React 讨论“props 变了没有”时,很多时候不是在做深比较,而是在利用这种身份差异快速判断。

最常见的几种情况

对象 props

<Child config={{ size: 'small' }} />

每次父组件执行时,这个对象都是新引用。

数组 props

<Child list={[1, 2, 3]} />

同样会不断生成新引用。

函数 props

<Child onClick={() => doSomething()} />

函数身份也会在每次渲染时变化。

怎么看待这个问题更合理

  • 不要一看到新引用就紧张
  • 只有子组件确实重、渲染频繁、且优化链路依赖稳定引用时,才值得处理

最短记忆方式

React 不只看“值像不像”,很多时候也会被“引用是不是同一个”影响。

面试要点

来自 react-prop-identity-interview-question 的面试视角整理。

一句话回答

因为对象、数组、函数在父组件每次重新执行时,常常都会生成新的引用;即使内容看起来一样,React.memo 也可能把它当成 props 已变化。

最稳的回答主线

  • 基本类型更容易保持稳定比较
  • 对象、数组、函数经常在每次渲染时变成新引用
  • 如果子组件优化链路依赖引用稳定,就可能需要 useMemouseCallback

一个更完整的面试表达

可以这样答:

React 里对象、数组、函数作为 props 时,问题不在“内容看起来一样”,而在“是不是同一个引用”。父组件每次重新执行时,内联对象和内联函数通常都会生成新引用,所以 React.memo 很可能把它们当成 props 已变化,导致子组件没法跳过重渲染。

一个典型例子

<Child config={{ size: 'small' }} />
<Child onClick={() => doSomething(id)} />

这两种写法在父组件每次渲染时,都会生成新引用。

一个加分点

如果面试官继续问底层原因,可以补一句:

这其实不是 React 特有问题,而是 JavaScript 的值语义决定的。原始值更接近按值比较,对象和函数更接近按身份比较。

什么时候才值得优化

不是一看到对象 props 就要 useMemo / useCallback

只有当:

  • 子组件真的比较重
  • 子组件已经用了 React.memo
  • 引用稳定性能换来实际收益

这时再优化才合理。

最短记忆方式

内容像一样,不代表引用还是同一个。

创建于 2026/3/19 更新于 2026/5/27