React 父组件更新为何会触发子组件重新渲染:React.memo 与对象引用
通过一个 React 父子组件示例,理解 state 更新、组件重新执行、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} />
</>
);
}请回答:
首次打开页面时,控制台会输出什么?
每次点击按钮后,
App和Profile是否都会重新执行?为什么?Profile已经使用了memo,为什么它仍可能重新渲染?如果希望
count更新时尽量避免Profile重新渲染,应该如何调整代码?是否应该为了避免所有子组件重新渲染,到处使用
memo和useMemo?为什么?
我的回答
- 首次输出:
App render
Profile renderApp会重新执行,因为点击按钮后count发生变化。React.memo是缓存组件,如果入参不变,就不会重新渲染。因为
Profile和App处于同一个父组件下。将
Profile移动到单独一个组件下。不应该,但不太清楚原因。
面试点评
总分 :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 !== 新的 userReact.memo 发现 props 中的 user 引用发生了变化,因此没有跳过渲染。
2. 移动到单独文件不能解决问题
如果 App 每次执行时仍然创建新的 user 对象,传入的引用仍然不同。
推荐答案与原理解析
首次渲染时会输出:
App render
Profile render点击按钮后:
count更新。App重新执行。App重新创建user对象。Profile接收到新的user引用。React.memo的浅比较判断 props 发生变化。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、并发渲染和高级性能架构。
相关文章
- 面试题-基础
Vue 3 响应式解构:为什么替换 user 后出现“李四 / 王五”
通过一道 Vue 3 代码题,区分 reactive 对象解构、嵌套属性修改与对象替换的行为,并用 toRef 保持源属性关联。文章保留首次作答及追问补答,说明从误判到纠正的过程。
- 面试题-基础
Vue 3 编辑弹窗:草稿状态、取消重开与父子数据契约
以一个始终挂载的姓名编辑弹窗为场景,给出覆盖数据归属、保存取消、重开初始化和外部更新冲突的完整 Vue 3 解法。结合首次回答与递进补答,说明本地草稿、脏标记和版本校验各自解决的问题。
- 面试题-基础
JavaScript 浅拷贝、深拷贝与 structuredClone:前端状态更新的正确方式
通过一道对象复制代码题,梳理 JavaScript 浅拷贝、JSON 深拷贝、structuredClone 和 transferable object 的边界,并解释 React 结构共享与 Vue 3 响应式更新的差异。
评论
0 条评论会在审核后展示,邮箱不会公开。