默认情况下,React 每次重新渲染时都会重新运行组件的整个主体。
优化重新渲染性能的常见方法是跳过不必要的工作。 例如,可以告诉 React 重用缓存的计算,或者如果数据自上次渲染以来没有更改,则跳过重新渲染。
要跳过计算和不必要的重新渲染,请使用以下 Hooks 之一:
-
useMemo
缓存计算结果。 -
useCallback
缓存函数定义,再传递给优化组件。
要优先考虑渲染,请使用以下 Hooks 之一: -
useTransition
将状态转换标记为非阻塞并允许其他更新中断它。 -
useDeferredValue
可以推迟更新 UI 的非关键部分,并让其他部分先更新。
useMemo
useMemo 会在重新渲染之间缓存calculateValue结果,直到dependencies发生变化,第二个参数不传的话,会每次都调用计算函数。
const cachedValue = useMemo(calculateValue, dependencies)
定义
参数
-
calculateValue
: 不带任何参数,例如 () =>,并返回想要计算的值。dependencies如果改变,会重新调用该函数。 -
dependencies
: 依赖项列表,包括组件中计算中使用的每个值。例如:[todos, tab](通过Object.is
比较dependencies两次的值。
返回值
在初始渲染中,“useMemo”返回不带参数调用“calculateValue”的结果。
在下一次渲染期间,它将返回上次渲染中已存储的值(如果依赖项未更改),或者再次调用“calculateValue”,并返回“calculateValue”返回的结果。
用法
1.跳过重新计算
1.当 List 的 props 与上次渲染时相同时,可以通过将其包装在 memo
:
中来告诉 List 跳过重新渲染:
import { useMemo, useState } from "react";
export function MemoDemo() {
const [name, setName] = useState("Tom");
const [todoList, setTodoList] = useState(["1", "2", "3"]);
const todoData: any = useMemo(() => {
return todoList.filter((item) => item != "1");
}, [todoList]);
return (
<div>
<div
onClick={() => {
setName("Lisa"); //click的时候, 不会在调用filter方法
}}
>
memo {name}
</div>
<List todoList={todoData}></List>
</div>
);
}
function List({ todoList }) {
return todoList.map((item) => <span key="item">{item}</span>);
}
2.记住另一个hook的依赖项
function Dropdown({ allItems, text }) {
const searchOptions = { matchMode: 'whole-word', text };
const visibleItems = useMemo(() => {
return searchItems(allItems, searchOptions);
}, [allItems, searchOptions]); // 🚩 注意: dependency是在组件中创建的object
// ...
上述dependency是在组件中创建的object,每次组件在渲染的时候都会重新创建一个searchOption对象,每次都是不同的searchOptions对象, 所以visibleItems每次渲染都会执行。
如何解决这个问题?可以将searchOptions对象也通过useMemo存储下。
function Dropdown({ allItems, text }) {
const searchOptions = useMemo(() => {
return { matchMode: 'whole-word', text };
}, [text]); // ✅ Only changes when text changes
const visibleItems = useMemo(() => {
return searchItems(allItems, searchOptions);
}, [allItems, searchOptions]); // ✅ Only changes when allItems or searchOptions changes
// ...
进一步的优化,可以将searchOptions对象放在useMemo计算方法内
function Dropdown({ allItems, text }) {
const visibleItems = useMemo(() => {
const searchOptions = { matchMode: 'whole-word', text };
return searchItems(allItems, searchOptions);
}, [allItems, text]); // ✅ Only changes when allItems or text changes
// ...
3.缓存一个方法
export default function ProductPage({ productId, referrer }) {
function handleSubmit(orderDetails) {
post('/product/' + productId + '/buy', {
referrer,
orderDetails
});
}
return <Form onSubmit={handleSubmit} />;
}
注意:{}, (){}, ()=> {},这种声明在重新渲染时, 会创建一个新的函数,那么对于缓存来说,会认为是不同的方法。
如何优化?
export default function Page({ productId, referrer }) {
const handleSubmit = useMemo(() => {
return (orderDetails) => {
post('/product/' + productId + '/buy', {
referrer,
orderDetails
});
};
}, [productId, referrer]);
return <Form onSubmit={handleSubmit} />;
}
这样看着有些繁琐,进一步优化,使用useCallback
export default function Page({ productId, referrer }) {
const handleSubmit = useCallback((orderDetails) => {
post('/product/' + productId + '/buy', {
referrer,
orderDetails
});
}, [productId, referrer]);
return <Form onSubmit={handleSubmit} />;
}
好了,那我们的useMemo到这里就结束了。
宝子们可以收藏评论交流哦
网友评论