Open monsterooo opened 4 years ago
这篇文章是我看到useCallback和useMemo讲的比较好的,可以转载否?另外,直接加上codepen的代码例子,就更好了
这篇文章是我看到useCallback和useMemo讲的比较好的,可以转载否?另外,直接加上codepen的代码例子,就更好了
嗯,可以转载的哈。
上面的代码部份是有codepen演示的哈,仔细看应该能发现😀
const handleCount = React.useCallback(() => setCount(count => count + 1), [count]);
该句加了[count]
之后,handleCount每次的值都会变。这代码其实没有做到性能优化。
const handleCount = React.useCallback(() => setCount(count => count + 1), [count]);
该句加了
[count]
之后,handleCount每次的值都会变。这代码其实没有做到性能优化。
是的,正确的应该是这样使用
const handleCount = React.useCallback(() => setCount(count => count + 1), []);
const handleCount = React.useCallback(() => setCount(count => count + 1), []);
这样还是有问题,useCallback
绑定的回调函数拿不到最新的count的值
@monsterooo
我同意@AlexZhong22c的说法。
这个依赖是要写的,useCallback只是说当依赖不变的时候,返回同一个函数,但是不能省略依赖的。
useCallback应该是说避免了其他的state对当前函数的影响。这个count的依赖项我认为是必须要的。
另外呢,还可以用useReducer去维护一个局部的redux,dispatch去做,见useReducer
const handleCount = React.useCallback(() => setCount(count => count + 1), []);
这样还是有问题,
useCallback
绑定的回调函数拿不到最新的count的值
感觉你还是没理解,count可以拿到最新的值(闭包作用域), 在这个例子中已经很明确的展示出来了。
如果还有撒疑问可以继续讨论
@monsterooo
我同意@AlexZhong22c的说法。
- 这个依赖是要写的,useCallback只是说当依赖不变的时候,返回同一个函数,但是不能省略依赖的
- useCallback应该是说避免了其他的state对当前函数的影响。这个count的依赖项我认为是必须要的。
- 另外呢,还可以用useReducer去维护一个局部的redux,dispatch去做,见useReducer
我看错了, setCount(count => count + 1) setCount是个函数,可以拿到最新的值。相当于以前的setState(prevState => prevState.count + 1)。 不过,useCallback(() => setCount(count => count + 1), [])第二个参数写空数组不合适的吧,毕竟官方文档描述认为函数中引用的项都应该出现在依赖中
@monsterooo
我同意@AlexZhong22c的说法。
- 这个依赖是要写的,useCallback只是说当依赖不变的时候,返回同一个函数,但是不能省略依赖的
- useCallback应该是说避免了其他的state对当前函数的影响。这个count的依赖项我认为是必须要的。
- 另外呢,还可以用useReducer去维护一个局部的redux,dispatch去做,见useReducer
我看错了, setCount(count => count + 1) setCount是个函数,可以拿到最新的值。相当于以前的setState(prevState => prevState.count + 1)。 不过,useCallback(() => setCount(count => count + 1), [])第二个参数写空数组不合适的吧,毕竟官方文档描述认为函数中引用的项都应该出现在依赖中
最佳实践还是按照官方的来没问题,因为那个比较适合大多数情况。
像我们讨论的这种情况(优化依赖和缓存函数),不是应用特别大或者组件特别多的时候不会有太大的问题。
但是如果我们在写代码之前就知道并且做一些优化,对于以后可能会出现的性能问题就会有所避免。
如果useCallback或者useMemo的依赖是引用对象呢?比如Object/Array,如果没有记错的话,每次它们都会触发重新计算
React.useEffect(() => { ref.current = value; }); 这些尽量都写上依赖,其实不写和写也是一样的
https://jancat.github.io/post/2019/translation-usememo-and-usecallback/
useCallback 和 useMemo的成本是:对于你的同事来说,你使代码更复杂了;你可能在依赖项数组中犯了一个错误,并且你可能通过调用内置的 hook、并防止依赖项和 memoized 值被垃圾收集,而使性能变差。如果你获得了必要的性能收益,那么这些成本都是值得承担的,但最好先测量一下。
如果useCallback或者useMemo的依赖是引用对象呢?比如Object/Array,如果没有记错的话,每次它们都会触发重新计算
同问,如果是对象的话会怎么比较呢?
结论
先说结论
useCallback
和useMemo
都可缓存函数的引用或值,但是从更细的使用角度来说useCallback
缓存函数的引用,useMemo
缓存计算数据的值。回顾
useCallback回顾
根据官网文档的介绍我们可理解:在
a
和b
的变量值不变的情况下,memoizedCallback
的引用不变。即:useCallback
的第一个入参函数会被缓存,从而达到渲染性能优化的目的。useMemo回顾
根据官方文档的介绍我们可理解:在
a
和b
的变量值不变的情况下,memoizedValue
的值不变。即:useMemo
函数的第一个入参函数不会被执行,从而达到节省计算量的目的。分析
useCallback分析
我做了这样一个简单示例,我们来分析一下现象。
我们重点看这一行
const handleCount = () => setCount(count + 1);
根据我们之前的理解,我们知道每次App组件渲染时这个
handleCount
都是重新创建的一个新函数。我们也可以通过比较上一次的
prevHandleCount
和本次的handleCount
。可以明确的知道每次渲染时handleCount
都是重新创建的一个新函数。问题:它有什么问题呢?当我们将
handleCount
作为props传递给其他组件时会导致像PureComponent
、shouldComponentUpdate
、React.memo
等相关优化失效(因为每次都是不同的函数)展示问题Gif:
为了解决上述的问题,我们需要引入
useCallback
,通过使用它的依赖缓存功能,在合适的时候将handleCount
缓存起来。我创建了一个简单示例,来看看是如何解决的吧。这次我们重点看这行
我使用
useCallback
来缓存了函数,依赖项(deps)是一个空数组它代表这个函数在组件的生成周期内会永久缓存
。因为我们的
handleCount
是一个缓存函数,所以当我们传递给经过React.memo
优化的组件AotherComponent
时不会触发渲染温馨提示:在选择
useCallback
的依赖(deps)时请经过仔细的考虑,比如下面这样的依赖是达不到最好的优化效果,因为当我们增加了一次count
时,handleCount
的引用就会更改。解决问题Gif,我们可以看到prevHandleCount等于handleCount,也没有多余的渲染:
useMemo分析
useMemo
和useCallback
几乎是99%像是,当我们理解了useCallback后理解useMemo就非常简单。他们的唯一区别就是:
useCallback
是根据依赖(deps)缓存第一个入参的(callback)。useMemo
是根据依赖(deps)缓存第一个入参(callback)执行后的值。你明白了吗? 如果还没明白我贴一下
useCallback
和useMemo
的源码你来看看区别。聪明的你一定看出来区别是啥了对吧。
useMemo
一般用于密集型计算大的一些缓存。下面我写了一个简单示例,来展示
useMemo
如何使用的。这次我们重点看这行,只有当
count
变量值改变的时候才会执行useMemo
第一个入参的函数。通过
useMemo
的依赖我们就可以只在指定变量值更改时才执行计算,从而达到节约内存消耗。总结
我们一起回顾了
useCallback
和useMemo
的基本使用方法,接着找到了影响性能的根本原因然后通过useCallback
如何去解决性能问题。最后我们学习了如何使用useMemo
去缓存了计算量密集的函数。我们还通过观察React Hooks源码观察了useCallback
和useMemo
最根本的区别,这让我们在开发时可以做出正确的选择。如果有错误请斧正
感谢阅读