避免不必要渲染: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 图表
  • 大型表单
  • 树形组件
相关推荐
Frank学习路上2 小时前
【C++】面试:面向对象与多态
c++·面试
aaaa954726652 小时前
终端与IDE形态Vibe Coding实测:主流AI编程工具迁移与迭代对比
javascript·react.js·ecmascript
星辰_mya2 小时前
限流、漏斗桶和令牌桶的区别
java·开发语言·面试·架构·高并发
(Charon)2 小时前
【C++ 面试高频:STL 容器 vector、map、unordered_map 总结】
开发语言·c++·面试
放下华子我只抽RuiKe52 小时前
FastAPI 全栈后端(七):测试与自动化
运维·前端·人工智能·react.js·前端框架·自动化·fastapi
Frank学习路上3 小时前
【C++】面试:内存管理
c++·面试
兰令水5 小时前
leecodecode【面试150】【2026.6.14打卡-java版本】
java·算法·面试
kyriewen12 小时前
Git Commit 前自动修复代码风格?配置 Husky + lint-staged,从此 CR 只聊逻辑
前端·git·面试
程序员二叉13 小时前
【JUC】ThreadLocal底层原理|内存泄漏|弱引用|跨线程传递方案
java·开发语言·面试·职场和发展·juc