useCallback
和 useMemo
是一样的东西,只是入参有所不同。
useCallback
缓存的是回调函数,如果依赖项没有更新,就会使用缓存的回调函数
useMemo
缓存的是回调函数的 return
,如果依赖项没有更新,就会使用缓存的 return
官网有这样一段描述,换言之 useCallback(fn, dependencies)
相当于 useMemo(() => fn, dependencies)
1 | // Simplified implementation (inside React) |
useCallback
useCallback
允许你在组件渲染之间保存 函数定义。
1 | const cachedFn = useCallback(fn, dependencies) |
参数
fn
:想要缓存的函数。此函数可以接受任何参数并且返回任何值。- React 将会在初次渲染⽽⾮调⽤时返回该函数。
- 当进⾏下⼀次渲染时,如果
dependencies
相⽐于上⼀次渲染时没有改变,那么 React 将会返回相同的函数。 - 否则,React 将返回在最新⼀次渲染中传⼊的函数,并且将其缓存以便之后使⽤。
- React 不会调⽤此函数,⽽是返回此函数。你可以⾃⼰决定何时调⽤以及是否调⽤。
dependencies
:有关是否更新fn
的所有响应式值的⼀个列表。- 响应式值包括
props
、state
,和所有在你组件内部直接声明的变量和函数。 - 依赖列表必须具有确切数量的项,并且必须像
[dep1, dep2, dep3]
这样编写。 - React 使⽤
Object.is
⽐较每⼀个依赖和它的之前的值。
- 响应式值包括
返回值
在初次渲染时, useCallback
返回你已经传⼊的 fn
函数
在之后的渲染中, 如果依赖没有改变, useCallback
返回上⼀次渲染中缓存的 fn
函数;否则返回这⼀次渲染传⼊的 fn
。
源码
1 | function mountCallback<T>(callback: T, deps: Array<mixed> | void | null): T { |
updateCallback
代码意图也很简单,如果依赖数组 deps
没有变化,或者 deps=[]
的情况下,会返回之前缓存的回调函数,否则就更新对应 fiber.memoizedState.hook.memoizedState
并返回新的回调函数。
useMemo
useMemo
是每次重新渲染的时候能够缓存计算结果的Hook。
1 | const cachedValue = useMemo(calculateValue, dependencies) |
1 | function mountMemo<T>( |
使用场景总结
useCallBack
不要每个函数都包一下,否则就会变成反向优化。useCallBack
本身就是需要一定性能的- 其次
useCallBack
并不能阻止函数重新创建, 由于使用useCallback
时,函数会作为实参传给useCallback
,所以无论怎样useCallback
包裹的函数都是会重新创建的,只是当useCallback
的依赖没有改变时返回的是缓存中的函数而已
useMemo
的关键是权衡。其目的是避免不必要的计算,但也要注意不要滥用,因为维持这些缓存值也是有开销的。最佳的做法是先写出清晰和可读的代码,然后在性能瓶颈出现时,再考虑优化。- 如果函数组件中某个值需要大量的计算才能得出,或者渲染某个组件时有昂贵的计算,都可以考虑使用
useMemo
进行包裹 - 当值作为别的 hooks 的依赖时,可以考虑使用
- 如果函数组件中某个值需要大量的计算才能得出,或者渲染某个组件时有昂贵的计算,都可以考虑使用
- 如果某个函数或者值是传递给子组件的
props
,可以考虑使用useCallback
或者useMemo
进行包裹(配合React.memo
使用)