React手册 Hooks 之 useCallback
描述
React 官网对
u
seCallback
的描述原文
u
seCallback
is a React Hook that lets you cache a function definition between re-renders.
useCallback
是一个 React Hook,可以在你重新渲染之间缓存函数的定义.
当你在渲染一个函数组件的时候, 如果在组件内部又声明了函数, 那么使用
u
seCallback
来包装这个内部函数可以缓存该函数引用, 避免每次重新渲染都获取到新函数.
场景
React: 默认情况下,当一个组件重新渲染时,React 会递归地重新渲染它的所有子组件
因此当父组件状态变化时, 很多时候大部分子组件都是不必重新渲染的, React.memo 就是专门解决这种问题的一个方法, 被 React.memo 包装后的组件, 每次渲染会对所有 props 浅对比, 如果没有变化, 则不进行重新渲染, 从而提升性能, 但是如果传递的 props 是一个函数, 且这个函数还是在父组件内部创建的, 那么父组件每次渲染时给子组件传递的函数都是一个新的函数引用, 这时 React.memo 会认为 props 发生变化并重新渲染, 起不到优化的作用.
u
seCallback
就是为了解决这个 React.memo 优化失效的问题, 它会在父组件外部缓存函数, 当父组件渲染时, 会返回函数的缓存, 让 React.memo 能拿到相同的函数引用, 从而让优化生效.
在类组件中, 因为可以通过 this 来获取同一个方法引用来避免这个问题.
接口定义:
参数
fn: Function
需要缓存的函数, React 并不会调用它, 通常情况下是一个需要传递给子组件的函数, 缓存的意义也是为了避免子组件不必要的渲染.
dependencies: Array<mixed>
缓存函数 fn 的时候, 需要告诉
u
seCallback
何时更新缓存,
dependencies
里的状态发生变化的时候, 就会使缓存的函数进行更新
当没有传,
dependencies
的时候,
u
seCallback
始终返回一个新函数, 这样也会让
React.memo
的优化失效, 所以一定要传, 当传入的是一个空数组的时候, 因为依赖项始终是空, 检测不到变化, 所以渲染之后缓存无法再更新.
返回
返回一个函数引用, 如果 dependencies
没有发生变化, 那么拿到的就是缓存中的函数引用, 反之会拿到一个新的函数引用.
用法1
配合
React.memo
对子组件的渲染做优化, 没有使用
React.memo
和
u
seCallback
的情况下, 两个子组件任意一个点击, 都会使整个
MyApp
组件和所有子组件重新渲染, 增加了优化之后, 每次点击
MemoMyButton
都只会让
MyApp
和当前点击的子组件重新渲染.
用法2
当传入 dependencies
后, 依赖的变化必然会导致函数缓存的更新, 有时这是不必要的, 也就是需要一种可以更新状态, 但不用更新函数缓存的方法,
u
seCallback
同样具有这样的效果.
在这个例子中, count 的变化, 始终会让 countClick 更新, 这同样会让 React.memo 失效, 显然 count 并不是一个合适的依赖项, 但是不传的话又会导致函数始终使用缓存中的状态, count 就会一直等于 1, 这种情况我们需要使用下面的写法.
这样的写法可以让
u
seCallback
始终返回缓存中的函数, 同时也可以正确更新
count
的状态.
用法3
u
seCallback
还可以配合 useEffect 使用,当 useEffect 的依赖是一个函数时,可以使用
u
seCallback
来缓存依赖函数,来避免
useEffect
不必要的执行
不过这种方式并不推荐使用,一个函数的依赖项会很奇怪,而且完全可以不使用
u
seCallback
达到效果,像下面这样改写:
总结
u
seCallback
是协助
React.memo
对子组件渲染优化的一个
hook;
u
seCallback
需要谨慎使用, 传入合适的
dependencies
是优化的关键;
u
seCallback
不应该用做功能, 只用来做优化;
不要在循环中直接使用
u
seCallback,
必要的话就写在循环的子组件中;
可以和
u
seEffect
一起使用,但是并不推荐;
dependencies
不传会始终返回新函数, 传空数组会在渲染一次之后一直缓存;