useMemo hooks介绍
useMemo hooks的语法如下
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
该hooks返回一个记忆过的值,也就是计算过的值
把“创建”函数和依赖项数组作为参数传入 useMemo,它仅会在某个依赖项改变时才重新计算 memoized 值。这种优化有助于避免在每次渲染时都进行高开销的计算。
传入 useMemo 的函数会在渲染期间执行。请不要在这个函数内部执行与渲染无关的操作,诸如副作用这类的操作属于 useEffect 的适用范畴,而不是 useMemo。
你可以把 useMemo 作为性能优化的手段,但不要把它当成语义上的保证。将来,React 可能会选择“遗忘”以前的一些 memoized 值,并在下次渲染时重新计算它们,比如为离屏组件释放内存。先编写在没有 useMemo 的情况下也可以执行的代码 —— 之后再在你的代码中添加 useMemo,以达到优化性能的目的。
推荐启用 eslint-plugin-react-hooks
中的 exhaustive-deps 规则。此规则会在添加错误依赖时发出警告并给出修复建议。
从上面可以看出,react useMemo hooks可以用来优化函数组件的运行效率
不同于 useCallback , useMemo 是用来“记忆”值,useCallback 用来“记忆”函数,React.memo用来包裹函数组件,避免重新渲染。
接下来通过实际例子来演示,比如我们有一个筛选列表,我们可以会这样实现
const Content = () => {
const [keyword, setKeyword] = useState("");
const filterList = initList.filter((i) => {
console.log("filter handle");
return new RegExp(keyword, "ig").test(i.name);
});
const handleKeywordChange = (e) => {
setKeyword(e.target.value);
};
return (
<div>
<div>
<input
value={keyword}
type="text"
placeholder="搜索"
onChange={handleKeywordChange}
/>
</div>
<ul>
{filterList.map((i) => (
<li key={i.id}>{i.name}</li>
))}
</ul>
</div>
);
};
看起来功能没什么毛病吧,但是正常的功能页面不止一个状态,我们比如新增一个loading字段,这里例子当作只是用来演示多个状态如何影响性能
const Content = () => {
const [loading, setLoading] = useState(false);
const [keyword, setKeyword] = useState("");
const filterList = initList.filter((i) => {
console.log("filter handle");
return new RegExp(keyword, "ig").test(i.name);
});
const handleKeywordChange = (e) => {
setKeyword(e.target.value);
};
return (
<div>
<div>
<input
value={keyword}
type="text"
placeholder="搜索"
onChange={handleKeywordChange}
/>
<button
onClick={() => {
setLoading(!loading);
}}
>
搜索
</button>
</div>
<ul>
{loading && <span>loading...</span>}
{filterList.map((i) => (
<li key={i.id}>{i.name}</li>
))}
</ul>
</div>
);
};
可以看出,每次更新loading值,filter计算都会重新被调用(通过console可以看出),这时候是不是就浪费了大量不必要都重新计算,这时候useMemo就可以派上用场了,只需要做下改动即可
const Content = () => {
const [loading, setLoading] = useState(false);
const [keyword, setKeyword] = useState("");
const filterList = useMemo(() => {
return initList.filter((i) => {
console.log("filter handle");
return new RegExp(keyword, "ig").test(i.name);
});
}, [keyword]);
const handleKeywordChange = (e) => {
setKeyword(e.target.value);
};
return (
<div>
<div>
<input
value={keyword}
type="text"
placeholder="搜索"
onChange={handleKeywordChange}
/>
<button
onClick={() => {
setLoading(!loading);
}}
>
搜索
</button>
</div>
<ul>
{loading && <span>loading...</span>}
{filterList.map((i) => (
<li key={i.id}>{i.name}</li>
))}
</ul>
</div>
);
};
这时候如果重新改变loading状态,我们可以看到filter不会重新计算了。是不是很happy。
具体效果可以通过以下代码来演示