React Memo
组件一多,「重复渲染」就成了性能杀手:父组件点一下按钮,所有子组件都跟着重新执行一遍,哪怕它们的数据根本没变。React 为此提供了 memo、useMemo、useCallback 三件套,相当于给渲染加缓存。本节讲清各自的职责与适用场景。
问题:父组件一更新,子组件全跟着跑
默认情况下,父组件的 state 变化触发自身重渲染时,子组件也会无条件重渲染。
import { useState } from 'react';
function Child({ name }) {
console.log('Child 执行了');
return <p>{name}</p>;
}
export default function App() {
const [count, setCount] = useState(0);
return (
<>
<button onClick={() => setCount(count + 1)}>计数:{count}</button>
<Child name="小明" />
</>
);
}
// 每次点击按钮,控制台都会打印:Child 执行了
React.memo:props 没变就跳过渲染
用 memo 包裹组件后,React 会先浅比较新旧 props,发现没变化就直接复用上次渲染结果,跳过整个渲染过程。
import { memo, useState } from 'react';
const Child = memo(function Child({ name }) {
console.log('Child 执行了');
return <p>{name}</p>;
});
// name 不变时再点按钮,控制台不再打印
注意:memo 只做浅比较。父组件每次渲染都新建对象或函数时,它们的引用每次都不同,memo 依然会失效,需要配合 useCallback、useMemo 稳定引用。
useMemo:缓存计算结果
函数组件每次渲染都会重新执行函数体,昂贵的计算应交给 useMemo,只在依赖变化时才重算。
import { useMemo } from 'react';
const count = useMemo(() => {
return list.filter((item) => item.price > 100).length;
}, [list]); // list 不变,结果直接复用上次的
useCallback:缓存函数引用
父组件每次渲染都会生成一个新函数,即使函数体完全一样。传给 memo 子组件的函数若用 useCallback 固定引用,memo 才能真正生效。
import { useCallback } from 'react';
const handleClick = useCallback(() => {
console.log('点击了文章', id);
}, [id]); // id 不变时,handleClick 始终是同一个引用
小结
三件套的职责一句话就能记住:memo 缓存组件,useMemo 缓存计算值,useCallback 缓存函数。它们都只为避免无谓渲染服务,自身也有比较开销,先确认渲染确实昂贵再使用,才是正确的打开方式。