理解 useMemo、useCallback 和 memo AI
5月 15, 2024
虽然 React 的官方文档已经写的很清楚了,但我还是想总结下。形而上地讲前两个是 Hook,而 memo
是 API。形而下地说,useMemo
、useCallback
和 React.memo
是 React 中用于优化性能的工具,它们帮助你避免不必要的重新渲染。
- https://zh-hans.react.dev/reference/react/useCallback
- https://zh-hans.react.dev/reference/react/useMemo
- https://zh-hans.react.dev/reference/react/memo
以下是它们的区别和使用示例。
1. useMemo
#
useMemo
用于记住计算结果,只有当依赖项发生变化时才重新计算。这对于昂贵的计算特别有用。
语法 #
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
示例 #
import React, { useMemo } from 'react';
function ExpensiveComponent({ a, b }) {
const expensiveValue = useMemo(() => {
// 假设这是一个昂贵的计算
return a + b;
}, [a, b]);
return <div>{expensiveValue}</div>;
}
值得注意的是,如果 a 或 b 是 useState,并且发生了变化,那么 expensiveValue 的改变也会触发重新渲染。
2. useCallback
#
useCallback
用于记住函数实例,只有当依赖项发生变化时才重新创建。这对于传递给子组件的回调函数特别有用,防止子组件不必要的重新渲染。
⚠️ 虽然每次函数的参数可能一样,但是每次要渲染子组件时因为函数实例的变化,还是会被 Object.is
认为传入了新的值,即会触发子组件的重新渲染,从而造成性能开销。更好的做法是不仅要使用 useCallback
,还要尽量使用 updater function 来消除 useCallback
函数的第二个参数的数量,使其为空最好,从而达到整个生命周期都不会触发重新渲染的效果。
语法 #
const memoizedCallback = useCallback(() => {
doSomething(a, b);
}, [a, b]);
示例 #
import React, { useCallback } from 'react';
function ChildComponent({ onClick }) {
return <button onClick={onClick}>Click me</button>;
}
function ParentComponent() {
const handleClick = useCallback(() => {
console.log('Button clicked');
}, []);
return <ChildComponent onClick={handleClick} />;
}
3. React.memo
#
React.memo
是一个高阶组件,用于记住组件的渲染结果,只有当 props 发生变化时才重新渲染。这对于函数组件特别有用,防止不必要的重新渲染。
语法 #
const MemoizedComponent = React.memo(Component);
示例 #
import React from 'react';
const ChildComponent = React.memo(({ value }) => {
console.log('ChildComponent rendered');
return <div>{value}</div>;
});
function ParentComponent() {
const [count, setCount] = React.useState(0);
const [value, setValue] = React.useState('Hello');
return (
<div>
<button onClick={() => setCount(count + 1)}>Increment Count</button>
<ChildComponent value={value} />
</div>
);
}
如何理解和选择 #
useMemo
:- 使用场景:当你有一个昂贵的计算,并且希望在依赖项不变的情况下避免重复计算。
- 使用方法:将昂贵的计算放在
useMemo
的回调函数中,并提供依赖项数组。
useCallback
:- 使用场景:当你需要将一个回调函数传递给子组件,并且希望在依赖项不变的情况下避免重新创建函数实例。
- 使用方法:将回调函数放在
useCallback
的回调函数中,并提供依赖项数组。
React.memo
:- 使用场景:当你有一个纯函数组件,并且希望在 props 不变的情况下避免重新渲染。
- 使用方法:将组件包裹在
React.memo
中。
总结 #
- 使用
useMemo
记住昂贵的计算结果。 - 使用
useCallback
记住回调函数实例。 - 使用
React.memo
记住组件的渲染结果。
通过合理使用这些工具,你可以显著优化 React 应用的性能,避免不必要的重新渲染。