memo是个API,useMemo是个Hook。这两个都可以进行性能的优化。作用就是缓存,一个相对于某个函数,一个相对于组件。
memo
组件在 props 没有改变的情况下跳过重新渲染。
import { memo, useState } from 'react';
export default function MyApp() {
const [name, setName] = useState('');
const [address, setAddress] = useState('');
return (
<>
<label>
Name{': '}
<input value={name} onChange={e => setName(e.target.value)} />
</label>
<label>
Address{': '}
<input value={address} onChange={e => setAddress(e.target.value)} />
</label>
<Greeting name={name} />
</>
);
}
const Greeting = memo(function Greeting({ name }) {
console.log("Greeting was rendered at", new Date().toLocaleTimeString());
return <h3>Hello{name && ', '}{name}!</h3>;
});
在第十行的input中输入东西,Greeting才会重新渲染,因为使用了memo,传入的props只有name。
useMemo
useMemo(calculateValue, dependencies)
calculateValue:要缓存计算值的函数,return计算结果。
dependencies:一个数组,如果 dependencies 没有发生变化,React 将直接返回相同值。使用 Object.is 将每个依赖项与其之前的值进行比较。
[!tip] Object.is(),非常严格的判断,带不同符号的0都视为不相等,与
===相比,对NaN的判断更加宽松。
看看官网的例子
import { useMemo } from 'react';
function TodoList({ todos, tab, theme }) {
const visibleTodos = useMemo(() => filterTodos(todos, tab), [todos, tab]);
// ...
}
只有todos, tab发生变化,filterTodos才会重新计算。
memo 与 useMemo结合使用
场景:
import React, { useMemo, useState } from 'react';
import { memo } from 'react';
// 一个昂贵的计算函数,我们不希望在每次渲染时都调用它
function expensiveComputation(numbers) {
return numbers.reduce((sum, num) => sum + num, 0);
}
// 组件
const ListComponent = ({ numbers }) => {
const [count, setCount] = useState(0);
// 使用useMemo来缓存计算结果
const total = useMemo(() => expensiveComputation(numbers), [numbers]);
return (
<div>
<h1>Total: {total}</h1>
<button onClick={() => setCount(count + 1)}>Click me ({count} clicks)</button>
</div>
);
};
// 使用memo来防止无关状态更新导致的重新渲染
export default memo(ListComponent);
useMemo确保只有当numbers数组改变时expensiveComputation函数才会重新计算。而memo确保只有当numbers数组本身发生改变时,ListComponent才会重新渲染。如果只是点击按钮改变count状态,组件不会进行不必要的重新渲染。
如果没有使用这种方式优化,函数和组件会进行多余的计算和渲染。