React Native Expo 急速教程 8 - useMemo
from:www.digitalocean.com/community/t… 翻译: 刘传君
你可能已经看到了,和React Hooks一起发布的一个奇怪的钩子,叫做useMemo。这个奇怪的钩子会是什么意思,它是用来做什么的?最重要的是,它能给你带来什么帮助,你为什么要了解它?
首先,先来刷一下JavaScript的引用相等性。
引用相等性
你可能还记得Javascript是如何比较对象🥴的。当我们进行平等比较时,会有一些棘手的结果。
{} === {} // false
const z = {}
z === z // true
React使用Object.is来比较组件,但这与使用===的结果非常相似。 因此,当React检查组件中的任何变化时,它可能会发现一个我们不会真正认为是变化的 "变化"。
() => {} === () => {} // false [] === [] // false
这个对比检查会造成一些我们不打算或不期望的React重渲染。如果这种重渲染是一些昂贵的操作,就会影响性能。如果一个部分重新渲染,就会重新渲染整个组件树。因此,React发布了备忘录的想法来解决这个问题。
Memo
你可能听说过这个花哨的词,memoization。Memoization基本上是一种优化技术,它将一个复杂的函数传入,让其被记忆或记忆。在memoization中,当后续传入相同的精确参数时,结果会被记住。有点像记忆法。如果我们让一个函数计算1+1,它将返回2。但如果它使用了memoization,下次我们通过函数运行1的时候,它就不会把它们加起来,它只会记住答案是2,而不会执行加法函数。
在React中,memoization优化了我们的组件,避免了复杂的重渲染,当它本来并非必要时。这里有一篇关于使用React.memo来优化你的应用的文章。React.memo的作用就像一个纯粹的组件,包装你的组件,链接的文章做了很好的解释。然而,useMemo在使用上有些不同。
从React官方文档来看,useMemo的签名是这样的。
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
正如你所看到的,useMemo接收了一个函数和一个依赖列表(数组[a,b])。如果你熟悉useEffect,那么你可能已经看到了依赖关系的概念。它们的作用类似于函数中的参数。依赖关系列表是useMemo观察的元素:如果没有变化,函数结果将保持不变,否则它将重新运行函数。如果它们没有变化,我们的整个组件重新渲染也没有关系,函数不会重新运行,而是返回存储的结果。如果封装的函数很大而且很贵,这可以是最佳的。这就是useMemo的主要用途。案例:
const List = useMemo(
() =>
listOfItems.map(item => ({
...item,
itemProp1: expensiveFunction(props.first),
itemProp2: anotherPriceyFunction(props.second)
})),
[listOfItems]
)
在上面的例子中,useMemo函数将在第一次渲染时运行。它会阻塞线程,直到昂贵的函数完成,因为useMemo在渲染中运行。最初,这看起来不像useEffect那么干净,因为useEffect可以渲染一个加载的spinner,直到昂贵的函数完成。然而,如果listOfItems从未发生变化,那么这些昂贵的函数就不会再被触发,我们仍然可以从它们那里得到返回值。这将使这些昂贵的函数看起来是瞬时的。如果你有一两个昂贵的同步函数,这是最理想的。
防止再渲染
如果你熟悉React的类-组件生命周期钩子shouldComponentUpdate,那么useMemo在防止不必要的重渲染方面也有类似的用法。比方说,我们有这样一个实例。
function BabyGator({fish, insects}) { // because what else do baby gators eat?
const dinner = {fish, insects};
useEffect(() => {
eatFunction(dinner);
}, [fish, insects])
}
function MamaGator() {
return <BabyGator fish='small edible fish' insects={15}>
}
这个效果非常好。我们的useEffect钩子会观察鱼和昆虫属性的传递情况(他很饿)。然而,这只对原子值有效。这是关键。
还记得回到谈论引用相等的问题吗?
[] === [] // false。
这正是像useMemo和useCallback这样的备忘钩子的作用。如果我们的insects属性是一个数组,我们可以把它放在useMemo钩子里,渲染之后,它就会把它引用为等价。如果一个函数或者其他非原生值在useEffect依赖中,它将重新创建一个新的数组,因为闭包,发现它不等。
显然,在这里,我们不需要useMemo,如果我们所做的一切只是在记忆一个数组。但是如果有一个昂贵的函数来计算这个数组,那么useMemo就会很有用!
啥时候不用useMemo
useCallback钩子与useMemo类似,但它返回的是一个备忘函数,而useMemo有一个返回值的函数。如果我们的依赖关系数组是空的,就不可能进行备忘录化,它将在每次渲染时计算一个新的值。这时你不妨实现useRef钩子。与useRef相比,useMemo提供的优势是,如果依赖关系发生变化,就会重新备忘。
在寻求实现useMemo的时候,总是要问:"这真的是一个昂贵的函数吗?" 昂贵意味着它在吸取大量的资源(比如内存)。如果你在渲染时在一个函数中定义了大量的变量,那么用useMemo来备忘可能是有意义的。
你不会想让useMemo触发任何副作用或任何异步调用。这两点放在useEffect中会更有意义。
当你想实现useMemo的时候,先写好代码,然后再重新审视它,看看是否可以优化它。不要一开始就用useMemo。太多的人很快就实现了它,它可能会让一个小应用的性能变差。
参考文献
和以往一样,最理想的是去看官方文档。从文档中,你会看到以下内容也是推荐的。
我们推荐使用 exhaustive-deps 规则作为 eslint-plugin-react-hooks 包的一部分。当依赖关系被错误指定时,它会发出警告,并建议修复。
转载自:https://juejin.cn/post/6857793214171873294