前端进阶之旅前端进阶之旅
  • 基础篇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 每日动态
  • 公众号动态公众号历史文章
  • 博客动态站长的技术博客
  • 开发者导航常用工具与文档站
首页程序员面试题库useMemo 不是保证 缓存可能被丢弃
ReReact性能与并发

为什么 useMemo 只是性能优化,不能用它保证业务正确性?

缓存丢了,重新计算一次仍应得到正确结果。如果丢缓存会造成重复连接或业务损坏,职责已经放错了位置。

前端进阶之旅 · 一题精讲更新于 2026.09.06
React#性能与并发#React 性能#缓存#性能优化
先看核心答案读代码示例
理解线索

按数据责任选择保存位置

  1. useMemo可丢弃、可重新求值的纯派生结果
  2. state 或 ref当前组件实例拥有的数据或可变句柄
  3. Effect组件与外部系统建立同步关系并负责解除

即便依赖数组为空,也不是全局只运行一次的承诺。支付、上报和写入等业务还需要自己的事件边界或幂等设计。

核心回答

先记住这个答案

useMemo 的作用是让 React 有机会复用纯计算结果,缓存并不是必须永久保留的业务状态。开发时编辑文件、初次挂载发生挂起等情况可能使缓存被丢弃,严格模式也会额外检查计算。不能把打开连接、登记订阅或保证仅执行一次的操作放进 useMemo。外部资源应在 Effect 中建立并返回清理函数;需要参与界面的数据放 state,不参与渲染的可变句柄按需要放 ref,并明确它们都受组件实例生命周期约束。

  • 缓存消失后重新计算仍应正确
  • 外部资源需要明确建立和清理
  • state 与 ref 也不能跨重新挂载永久保存

渲染尝试不等于成功挂载

React 可能开始渲染一个组件,却因挂起或其他调度原因没有提交这一轮结果。如果 useMemo 的计算在这时打开了连接,就可能出现资源已创建、对应组件却没有完成挂载的情况,清理路径也难以对应。

纯计算没有这个问题:被放弃的筛选结果可以丢掉,之后再算一次即可。把性能缓存和资源所有权分开,才能允许 React 重试渲染,而不会把渲染次数变成服务端连接数或业务写入次数。

让连接跟随真实目标,并配对清理

房间连接依赖 roomId 与 connect。把选项对象直接放进 Effect,主题变化不会仅因新建选项对象而重连;房间或连接实现改变时,旧连接关闭后再建立新连接,卸载时也有明确清理。

connect 应返回代表这次连接的关闭句柄,失败策略由连接层明确处理。这个最小示例假定连接建立同步成功,只展示所有权配对;实际异步握手还需要处理取消、超时以及旧请求晚到的问题。

由 Effect 拥有房间连接TSX
import { useEffect } from 'react';

type Connect = (options: { roomId: string }) => { close: () => void };

export default function RoomConnection({ roomId, theme, connect }: {
  roomId: string;
  theme: 'light' | 'dark';
  connect: Connect;
}) {
  useEffect(() => {
    const connection = connect({ roomId });
    return () => connection.close();
  }, [roomId, connect]);

  return <p data-theme={theme}>当前房间:{roomId}</p>;
}

这个资源示例刻意不需要 useMemo:Effect 内创建配置,依赖真正决定连接的值。验收应包含只换主题、换房间、换连接函数和卸载,确认每次建立的连接都有对应关闭。

真正需要保存的数据要有明确所有者

用户正在编辑的草稿应存入状态,不能只放在会被丢弃的派生缓存里。不影响界面的实例句柄可以用 ref 保存,但写入时机仍需符合渲染纯度;持久化到组件之外的数据则需要明确的外部存储。

state 和 ref 的存续以当前组件身份为前提。换 key、卸载再挂载或离开页面后,它们不会天然保留;如果希望草稿跨路由恢复,应另行设计更高层状态或本地持久化,而不是把某一种 Hook 宣称为永久保险箱。

回答前,多想一步

容易答错的地方

在 useMemo 内创建必须清理的资源
缓存函数没有与每一次调用一一对应的资源清理协议。把 WebSocket、订阅或定时器创建塞进去,可能在重试期间泄漏;应该在可清理的副作用中管理,并让建立与解除围绕同一资源句柄。
用空依赖数组承诺业务只执行一次
空数组只描述响应式依赖,并不约束重新挂载和开发检查。对于不可重复的业务动作,应从用户事件或明确任务触发,使用业务标识和服务端幂等保证,不能依赖某个组件函数恰好只运行一回。
试着用自己的话回答

面试官还会怎么问?

把 memo 对象作为 Effect 依赖总是错误吗?

不是总是错误,但 Effect 会依赖缓存对象身份,缓存丢弃后可能重新同步。若对象仅为该 Effect 服务,移入 Effect 并依赖基本值更直接;若确有共享用途,也应确保重新同步可以安全清理和重建。

开发严格模式为什么会重复建立连接?

开发检查可能执行额外的建立、清理、再建立过程,用来暴露缺失清理。正确连接逻辑应能配对释放并再次建立;不能通过隐藏第二次执行来掩盖泄漏,否则真正卸载重挂时仍会出问题。

能把昂贵初始化改放 useState 吗?

如果结果属于组件拥有的状态,可以采用惰性初始化,但初始化函数仍必须纯粹,开发检查或重新挂载可能再次执行。外部资源副作用不能仅靠从 useMemo 换到 useState 就获得正确的生命周期。

从一道题,走向一组知识

把知识连起来

性能与并发

useMemo 的缓存和依赖数组是如何工作的?

先理解依赖相等时复用计算结果的通常行为。

性能与并发

useMemo 和 useCallback 有什么区别,应该如何选择?

区分值缓存和函数引用缓存各自能保证什么。

Hooks 与复用

eslint-plugin-react-hooks 能发现哪些错误,规则应该怎么配置?

使用规则检查渲染纯度、依赖与错误的 Hook 使用。

参考资料

  • React 官方:useMemo 注意事项
  • React 官方:useEffect

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

本题目录
  1. 先记住这个答案
  2. 渲染尝试不等于成功挂载
  3. 让连接跟随真实目标,并配对清理
  4. 真正需要保存的数据要有明确所有者
  5. 容易答错的地方
  6. 面试官还会怎么问
  7. 把知识连起来
读懂,再试着讲出来

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

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