跳到主要内容
← 返回文章列表
面试题-基础2026/09/06约 4 分钟读完

React 父组件更新为何会触发子组件重新渲染:React.memo 与对象引用

通过一个 React 父子组件示例,理解 state 更新、组件重新执行、React.memo 浅比较以及对象引用变化之间的关系。

React 父组件更新为何会触发子组件重新渲染:React.memo 与对象引用
文章目录 11

题目信息

  • 领域 :React、组件渲染、性能基础

  • 难度 :L1 基础认知(初级)

  • 题型 :代码题、原理题

  • 预计用时 :10–15 分钟

  • 考察方向 :理解 React 组件重新渲染、 和对象引用的基础机制,并判断记忆化优化是否真正有效。

面试题

import { memo, useState } from "react";

type ProfileProps = {
  user: {
    name: string;
  };
};

const Profile = memo(function Profile({ user }: ProfileProps) {
  console.log("Profile render");

  return <p>用户:{user.name}</p>;
});

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

  const user = {
    name: "Ada",
  };

  console.log("App render");

  return (
    <>
      <button onClick={() => setCount(count + 1)}>
        count: {count}
      </button>

      <Profile user={user} />
    </>
  );
}

请回答:

  1. 首次打开页面时,控制台会输出什么?

  2. 每次点击按钮后,App 和 Profile 是否都会重新执行?为什么?

  3. Profile 已经使用了 memo,为什么它仍可能重新渲染?

  4. 如果希望 count 更新时尽量避免 Profile 重新渲染,应该如何调整代码?

  5. 是否应该为了避免所有子组件重新渲染,到处使用 memo 和 useMemo?为什么?

我的回答

  1. 首次输出:
App render
Profile render
  1. App 会重新执行,因为点击按钮后 count 发生变化。React.memo 是缓存组件,如果入参不变,就不会重新渲染。

  2. 因为 Profile 和 App 处于同一个父组件下。

  3. 将 Profile 移动到单独一个组件下。

  4. 不应该,但不太清楚原因。

面试点评

  • 总分 :39 / 100

  • 正确性 :17 / 40

  • 原理理解 :7 / 20

  • 工程应用 :5 / 20

  • 边界与权衡 :2 / 10

  • 面试表达 :8 / 10

正确点

  • 首次渲染输出判断正确。

  • 知道 count 更新会让 App 重新执行。

  • 知道 React.memo 与 props 是否变化有关。

  • 能承认自己暂时不了解记忆化优化的边界。

遗漏点

App 每次执行时都会重新创建一个 user 对象:

const user = {
  name: "Ada",
};

虽然对象内容相同,但每次创建的都是不同引用。

还遗漏了:

  • React.memo 默认进行浅比较。

  • 移动组件文件不会自动改变对象引用。

  • memo、useMemo 会增加比较、缓存和维护成本。

  • 是否优化应该根据组件实际渲染成本和性能数据决定。

错误点

1. 子组件重渲染不是因为“和父组件处于同一层”

普通子组件可能随着父组件更新而重新执行,但这里真正导致 Profile 继续渲染的原因是:

旧的 user !== 新的 user

React.memo 发现 props 中的 user 引用发生了变化,因此没有跳过渲染。

2. 移动到单独文件不能解决问题

如果 App 每次执行时仍然创建新的 user 对象,传入的引用仍然不同。

推荐答案与原理解析

首次渲染时会输出:

App render
Profile render

点击按钮后:

  1. count 更新。

  2. App 重新执行。

  3. App 重新创建 user 对象。

  4. Profile 接收到新的 user 引用。

  5. React.memo 的浅比较判断 props 发生变化。

  6. Profile 重新执行。

这里比较的是引用,而不是对象内容:

const a = { name: "Ada" };
const b = { name: "Ada" };

a === b; // false

如果要稳定引用,可以根据实际情况:

  • 将固定对象定义在组件外部。

  • 对计算得到的对象使用 useMemo。

  • 直接传递稳定的基本类型 props。

但不应该到处使用 memo 和 useMemo。它们也有比较、缓存和维护成本,只有在组件渲染成本较高、父组件频繁更新且 props 能稳定时,才值得使用。

更适合面试口述的表达是:

点击按钮后,count 更新导致 App 重新执行。执行过程中会创建一个新的 user 对象。虽然对象内容相同,但引用不同。React.memo 默认进行浅比较,因此认为 props 发生变化,Profile 仍然会重新渲染。移动组件文件不能自动稳定引用,是否使用 memo 或 useMemo 应根据实际性能问题决定。

递进追问与补答

追问一:对象引用相等性

追问:

const a = { name: "Ada" };
const b = { name: "Ada" };

console.log(a === b);

输出是什么?结合这个结果,解释为什么 React.memo 没有跳过 Profile。

我的补答:

输出是 false,因为对象类型的引用不同。因此,React.memo 会判断每次创建的对象都不同,导致组件重复渲染。

补答评分: 86 / 100

补答点评:

判断正确:

  • 两个内容相同的对象使用 === 比较时结果为 false。

  • App 每次渲染都会创建新的 user 对象。

  • React.memo 发现新旧 props 的对象引用不同,因此继续渲染 Profile。

“引用链路不同”建议改成更准确的说法:

两个对象的引用不同,或者它们不是同一个对象。

最终总结

本题核心关系是:

state 更新
-> 父组件重新执行
-> 每次创建新的对象 props
-> memo 浅比较发现引用变化
-> 子组件继续渲染

需要区分:

  • 父组件重新执行,不代表所有子组件一定重新执行。

  • React.memo 比较的是 props 引用,不是对象内部内容。

  • 将组件移动到其他文件,不会自动稳定 props 引用。

  • memo 和 useMemo 是有成本的优化手段,不应无条件使用。

本题已经掌握了对象引用不同导致 React.memo 失效的核心原因;React 后续继续安排基础入门题,暂不涉及复杂 Hooks、并发渲染和高级性能架构。

评论

0 条

评论会在审核后展示,邮箱不会公开。

正在确认登录状态

5000 / 5000