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。