Content #
对于一个显示用户信息的列表,现在需要对用户名进行搜索,且 UI 上需要根据搜索关键字显示过滤后的用户,那么这样一个功能需要有两个状态:
- 用户列表数据本身:来自某个请求。
- 搜索关键字:用户在搜索框输入的数据。
无论是两个数据中的哪一个发生变化,都需要过滤用户列表以获得需要展示的数据。那么如果不使用 useMemo 的话,就需要用这样的代码实现:
import React, { useState, useEffect } from "react";
export default function SearchUserList() {
const [users, setUsers] = useState(null);
const [searchKey, setSearchKey] = useState("");
useEffect(() => {
const doFetch = async () => {
// 组件首次加载时发请求获取用户数据
const res = await fetch("https://reqres.in/api/users/");
setUsers(await res.json());
};
doFetch();
}, []);
let usersToShow = null;
if (users) {
// 无论组件为何刷新,这里一定会对数组做一次过滤的操作
usersToShow = users.data.filter((user) =>
user.first_name.includes(searchKey),
);
}
return (
<div>
<input
type="text"
value={searchKey}
onChange={(evt) => setSearchKey(evt.target.value)}
/>
<ul>
{usersToShow &&
usersToShow.length > 0 &&
usersToShow.map((user) => {
return <li key={user.id}>{user.first_name}</li>;
})}
</ul>
</div>
);
}
组件的任何一次渲染,都需要进行一次过滤的操作。但其实只需要在 users 或者 searchKey 这两个状态中的某一个发生变化时,重新计算获得需要展示的数据就行了。这个时候,可以用 useMemo 这个 Hook 来实现这个逻辑,缓存计算的结果:
//...
// 使用 userMemo 缓存计算的结果
const usersToShow = useMemo(() => {
if (!users) return null;
return users.data.filter((user) => {
return user.first_name.includes(searchKey));
}
}, [users, searchKey]);
//...
这是 userMemo 的一大好处:避免重复计算。
除了避免重复计算之外,useMemo 还有一个很重要的好处:避免子组件的重复渲染。比如在例子中的 usersToShow 这个变量,如果每次都需要重新计算来得到,那么对于 UserList 这个组件而言,就会每次都需要刷新,因为它将 usersToShow 作为了一个属性。而一旦能够缓存上次的结果,就和 useCallback 的场景一样,可以避免很多不必要的组件刷新。
Viewpoints #
From #
04|内置 Hooks(2):为什么要避免重复定义回调函数?