Skip to content

React.memo、useMemo、useCallback:别为了「优化」而优化

知识背景

React 默认父组件重渲染,子组件也会重渲染(除非子组件被 memo 包一层且 props 浅比较相等)。

  • React.memo:包函数组件,props 浅比较不变则跳过渲染。
  • useMemo:缓存计算结果(返回值),依赖变才重算。
  • useCallback:缓存函数引用,依赖变才新建函数。

三者都服务于减少无效渲染稳定引用(例如下游 memo 子组件、或 effect 依赖函数),但滥用会增加心智成本与内存,应先 profiling 再下手


知识详解与通俗解释

1. React.memo

jsx
const Row = React.memo(function Row({ title }) {
  return <div>{title}</div>;
});

仅浅比较 props。需要深度比对可传第二个参数自定义比较函数(注意性能)。子组件很重、且经常因父级无关 state 重渲染时性价比高。

2. useMemo

jsx
const sorted = useMemo(() => items.slice().sort(byDate), [items]);

避免每次渲染都对大列表排序。若计算很轻,useMemo 本身也有开销,可能得不偿失。

3. useCallback

jsx
const onSave = useCallback(() => { api.save(id); }, [id]);
return <MemoChild onSave={onSave} />;

当子组件被 memo 包裹且依赖回调引用相等时,useCallback 才有意义。否则只是多包一层。

4. 记忆口诀

  • memo:挡子组件重渲染。
  • useMemo:省重复计算。
  • useCallback:稳定函数引用,常和子 memo、或 useEffect 依赖配合。

总结

  • 三件套都是性能与引用稳定性工具,不是语法糖摆设。
  • 默认简单写;出现可测的性能问题或明确的依赖链需求再加。
  • 浅比较记不住时:新对象/新数组字面量每次渲染都是新引用,会击穿 memo