避免不必要渲染:PureComponent、memo、useMemo、useCallback

这是 React 面试中高频考点,很多人会背概念,但面试官更喜欢问:

PureComponent、memo、useMemo、useCallback 有什么区别?什么时候用?为什么能减少渲染?

你可以这样理解:


React 渲染原理

父组件重新渲染时:

javascript 复制代码
function Parent() {
  const [count, setCount] = useState(0);

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

点击按钮后:

复制代码
Parent render
Child render

即使 Child 的 props 没变:

xml 复制代码
<Child />

React 默认仍会重新执行 Child 函数。

这就是性能浪费。


1. PureComponent

类组件专用

scala 复制代码
class Child extends React.PureComponent {
  render() {
    console.log("child render");
    return <div>Child</div>;
  }
}

相当于:

javascript 复制代码
shouldComponentUpdate(
  nextProps,
  nextState
){
  return false;
}

但实际上内部做的是:

vbnet 复制代码
浅比较(Shallow Compare)

比较:

ini 复制代码
oldProps === newProps
oldState === newState

相同:

复制代码
不更新

不同:

复制代码
重新渲染

2. React.memo

函数组件版本 PureComponent

javascript 复制代码
const Child = React.memo(() => {
  console.log("child render");

  return <div>Child</div>;
});

父组件:

javascript 复制代码
function Parent() {
  const [count, setCount] = useState(0);

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

      <Child />
    </>
  );
}

点击按钮:

复制代码
Parent render

Child 不会重新渲染。


React.memo原理

内部也是:

scss 复制代码
shallowEqual(oldProps, newProps)

浅比较。


memo失效场景

ini 复制代码
<Child user={{ name: "Tom" }} />

每次 render:

diff 复制代码
{}
!== 
{}

引用变了。

所以:

复制代码
Child render
Child render
Child render

memo失效。


3. useMemo

缓存值

很多人误以为:

复制代码
useMemo 防止组件渲染

错。

它缓存的是:

复制代码
计算结果

例如

ini 复制代码
const total = useMemo(() => {
  return bigData.reduce(...)
}, [bigData]);

第一次:

复制代码
执行reduce

之后:

复制代码
直接返回缓存结果

不会重复计算。


不用 useMemo

ini 复制代码
const total = bigData.reduce(...)

Parent 每 render 一次:

复制代码
reduce执行一次

数据大时非常耗性能。


useMemo + memo

经典组合:

ini 复制代码
const user = useMemo(() => {
  return {
    name: "Tom"
  };
}, []);

<Child user={user}/>

这样:

sql 复制代码
user引用固定

memo 才能生效。


4. useCallback

缓存函数

很多人写:

ini 复制代码
<Child
  onClick={() => {
    console.log("click");
  }}
/>

每次 render:

javascript 复制代码
new Function()

都会生成新函数。


即使用了 memo:

ini 复制代码
const Child = memo(...)

也会重新渲染。

因为:

yaml 复制代码
oldFn !== newFn

解决:

ini 复制代码
const handleClick = useCallback(() => {
  console.log("click");
}, []);
ini 复制代码
<Child onClick={handleClick}/>

这样:

复制代码
函数引用固定

memo 生效。


useMemo vs useCallback

很多面试官喜欢问。

本质:

scss 复制代码
useCallback(fn,deps)

等于:

scss 复制代码
useMemo(() => fn, deps)

源码思想就是这样。


缓存值:

ini 复制代码
const user = useMemo(
  () => ({ name: "Tom" }),
  []
);

缓存函数:

ini 复制代码
const fn = useCallback(
  () => {},
  []
);

面试总结

API 作用 适用
PureComponent 类组件避免重复渲染 Class
React.memo 函数组件避免重复渲染 Function
useMemo 缓存计算结果
useCallback 缓存函数引用 函数

记住一句话:

复制代码
PureComponent / memo
解决「组件重复渲染」

useMemo
解决「重复计算」

useCallback
解决「函数引用变化导致的重复渲染」

实际项目中的最佳实践

在你做 React 管理后台(Ant Design、ECharts、大表格)时,最常见的优化组合是:

ini 复制代码
const columns = useMemo(() => [...], []);

const handleSearch = useCallback(() => {
  ...
}, []);

const TableList = memo(TableListComponent);

特别是:

  • Ant Design Table
  • ECharts 图表
  • 大型表单
  • 树形组件
相关推荐
抱抱宝1 小时前
Agent-study项目教程(03):手写 Mini-ReAct Agent(不依赖框架)
javascript·人工智能·gpt·react.js·prompt·agent
ShineWinsu3 小时前
对于C++:C++11中lambda、function、bind的解析
c++·面试·笔试·开发·lambda·bind·function
码匠许师傅4 小时前
【C++ 面试真题】聊聊 C++ 的拷贝构造与拷贝赋值
java·c++·面试
sakiko_5 小时前
OC学习笔记-面试题
面试·objective-c
Heo6 小时前
大厂前端调试不能只会debugger
前端·javascript·面试
想要成为糕糕手6 小时前
🚀 在浏览器里跑 DeepSeek-R1?WebGPU 端侧推理实战(六)—— 完结篇:消息渲染与流式收尾
react.js·typescript·llm
城管不管6 小时前
重生——第九次面试2026.8.13某车一面
分布式·ai·面试·职场和发展·rabbitmq
GitLqr7 小时前
玩转 Flutter 中的 Stack 与 Positioned:解决 UI 重叠问题的实战指南
flutter·面试·全栈
windliang7 小时前
Claude Code 源码分析(十二):错误处理与自动恢复:让 Agent 稳定运行
前端·javascript·面试
2601_949950637 小时前
Java 面试准备,用练题簿把“背过八股”变成“真正会答”
java·开发语言·面试·刷题·小程序推荐