搞清楚 React.memo、useMemo 和 useCallback 什么时候真正让你的应用变快,什么时候只是白白让它变复杂。
在 Kodokon 中打开本课当一个组件重新渲染时,它的所有子组件默认都会重新渲染,无论它们的 prop 有没有变化。这并不是灾难:一次渲染只有在输出改变时才会触碰 DOM。当频繁的渲染碰上开销大的子树时,记忆化才变得有意义。三个工具,三个目标:React.memo 在组件的 prop 浅相等时跳过其渲染,useMemo 缓存一个计算出来的值,useCallback 缓存一个函数。三者都依赖引用相等。
import { memo } from "react";
const Row = memo(function Row({ item, onSelect }) {
return (
<li onClick={() => onSelect(item.id)}>{item.label}</li>
);
});链条的强度取决于它最薄弱的一环。如果哪怕只有一个 prop 在每次渲染时收到新引用——一个内联箭头函数、一个对象或数组字面量——memo 的比较就会失败,组件照样重新渲染。useCallback 稳定函数的引用,从而让比较得以成功。
import { useCallback } from "react";
function List({ items }) {
const handleSelect = useCallback((id) => {
console.log("selected:", id);
}, []);
return (
<ul>
{items.map((item) => (
<Row
key={item.id}
item={item}
onSelect={handleSelect}
/>
))}
</ul>
);
}useMemo 有两种正当用途:避免一次真正开销大的计算(对成千上万条数据排序或聚合),以及稳定一个引用,供 memo 化的子组件消费或列在某个 effect 的依赖里。对于一个微不足道的计算,缓存的成本超过它节省的:依赖比较、占用内存、读起来更费劲。
import { useMemo } from "react";
function Stats({ orders }) {
const total = useMemo(() => {
return orders.reduce(
(sum, order) => sum + order.amount,
0
);
}, [orders]);
return <p>Total: {total} €</p>;
}