useMemo:
useMemo: 用于性能优化。
依赖项发生变化时,才会重新计算,避免重复渲染,减少资源消耗。类似于vue的computed.
注意
- 如果计算逻辑简单,使用 useMemo 的开销可能比重新计算还大
执行时机
1.如果依赖项是个空数组,那么
useMemo的回调函数会执行一次2.指定依赖项,当依赖项发生变化时,
useMemo的回调函数会执行3.不指定依赖项,不推荐这么用,因为每次渲染和更新都会执行
React组件渲染条件:
1.组件的state发生变化
2.组件的props发生变化
3.useCOntext发生变化
案例
当我们使用 useMemo 缓存后,只有 goods 发生变化时, total 才会重新计算, 而 search 发生变化时, total 不会重新计算。
import React, { useMemo, useState } from 'react';
function App() {
const [search, setSearch] = useState('');
const [goods, setGoods] = useState([
{ id: 1, name: '苹果', price: 10, count: 1 },
{ id: 2, name: '香蕉', price: 20, count: 1 },
{ id: 3, name: '橘子', price: 30, count: 1 },
]);
const handleAdd = (id: number) => {
setGoods(goods.map(item => item.id === id ? { ...item, count: item.count + 1 } : item));
}
const handleSub = (id: number) => {
setGoods(goods.map(item => item.id === id ? { ...item, count: item.count - 1 } : item));
}
const total = useMemo(() => {
console.log('total');
return goods.reduce((total, item) => total + item.price * item.count, 0)
}, [goods]); //依赖项
return (
<div>
<h1>父组件</h1>
<input type="text" value={search} onChange={(e) => setSearch(e.target.value)} />
<table border={1} cellPadding={5} cellSpacing={0}>
<thead>
<tr>
<th>商品名称</th>
<th>商品价格</th>
<th>商品数量</th>
</tr>
</thead>
<tbody>
{goods.map(item => <tr key={item.id}>
<td>{item.name}</td>
<td>{item.price * item.count}</td>
<td>
<button onClick={() => handleAdd(item.id)}>+</button>
<span>{item.count}</span>
<button onClick={() => handleSub(item.id)}>-</button>
</td>
</tr>)}
</tbody>
</table>
<h2>总价:{total}</h2>
</div>
);
}
export default App;
这个例子没有使用 useMemo 进行缓存,所以每次 search 发生变化, total 都会重新计算,这样就造成了没必要的计算所以我们可以使用 useMemo 缓存,因为我们的 total 跟 search 没有关系,那么如果计算的逻辑比较复杂,就造成了性能问题。
import React, { useMemo, useState } from 'react';
function App() {
const [search, setSearch] = useState('');
const [goods, setGoods] = useState([
{ id: 1, name: '苹果', price: 10, count: 1 },
{ id: 2, name: '香蕉', price: 20, count: 1 },
{ id: 3, name: '橘子', price: 30, count: 1 },
]);
const handleAdd = (id: number) => {
setGoods(goods.map(item => item.id === id ? { ...item, count: item.count + 1 } : item));
}
const handleSub = (id: number) => {
setGoods(goods.map(item => item.id === id ? { ...item, count: item.count - 1 } : item));
}
const total = () => {
console.log('total');
//例如很复杂的计算逻辑
return goods.reduce((total, item) => total + item.price * item.count, 0)
}
return (
<div>
<h1>父组件</h1>
<input type="text" value={search} onChange={(e) => setSearch(e.target.value)} />
<table border={1} cellPadding={5} cellSpacing={0}>
<thead>
<tr>
<th>商品名称</th>
<th>商品价格</th>
<th>商品数量</th>
</tr>
</thead>
<tbody>
{goods.map(item => <tr key={item.id}>
<td>{item.name}</td>
<td>{item.price * item.count}</td>
<td>
<button onClick={() => handleAdd(item.id)}>+</button>
<span>{item.count}</span>
<button onClick={() => handleSub(item.id)}>-</button>
</td>
</tr>)}
</tbody>
</table>
<h2>总价:{total()}</h2>
</div>
);
}
export default App;