简析useCallback、useMemo和useRef的使用

122 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第15天,点击查看活动详情

先上结论

  • 如果想传递函数给子组件,请使用useCallback包裹后再传,避免子组件不必要的重复渲染,前提是子组件使用memo函数包裹
  • 如果想进行大量计算,请使用useMemo包裹计算逻辑后再使用其返回值,避免不必要的大量重复相同计算
  • 如果想传递对象给子组件,请使用useMemo包裹后再传,避免子组件不必要的重复渲染,前提是子组件使用memo函数包裹

问题:React组件在stateprops发生变化的时候都会从头到尾重新渲染一次,而在次渲染的时候,里面普通定义的函数、对象都会重新创建一次

一、useCallback

useCallback实际目的是为了进行性能的优化,如何进行性能优化呢?

useCallback会返回一个函数的memoized记忆值,在依赖不变的情况下,多次定义的时候返回的值是相同的

 const memoizedCallback = useCallback( () => {
   doSomething(a, b)
 }, [a, b])

对于定义一个函数本身来说,使用和不使用并不会带来性能优化,其优化的点在于,将把这个函数传递给子组件时,如果该函数不发生变化,那么子组件不会重新渲染!这就是性能优化的点,我们使用useCallback的目的是不希望子组件进行多次渲染,并不是为了函数进行缓存。

二、useMemo

useMemo实际目的是也为了进行性能的优化,如何进行性能优化呢?

useMemo返回的也是一个 memoized(记忆的)值,在依赖不变的情况下,多次定义的时候返回的值是相同的

 const memoizedValue = useMemo( () => {
   computeValue(a, b)
 }, [a, b])

如果不使用useMemo,在进行大量计算操作时,每次渲染组件都会重新计算一次,使用了useMemo后,如果依赖不变,就不会重新计算!这就是优化的点,同时,对子组件传递相同内容的对象时,使用useMemo也可以进行性能优化。

 const info = useMemo( () => ({
   name: 'coder',
   age: 18
 }))
 ​
 // 传递给子组件,在父组件发生重新渲染时,子组件不会重新渲染
 <Son info={info}/>

三、等价转换

从上面可以看出,如果useMemo返回一个函数,那和useCallback其实是一样的

useCallback(fn, deps)useMemo( ()=>fn, deps)是等价的!

四、useRef

ueeRef返回一个ref对象,返回的Ref对象在组件的整个生命周期保持不变

最常见的两种用法:

  • 用法一:引入DOM(或者组件,但是需要是class组件)元素
  • 用法二:保存一个数据,这个对象在整个生命周期中可以保持不变

该对象的值保存在current属性中

绑定DOM

 const titleRef = useRef()
 // DOM保存在titleRef.current中
 // 使用
 <h1 ref={titleRef}>title</h1>

保存一个数据

使用其特性,对象在组件的整个生命周期内保持不变,可以结合useCallback来使用,使用ref对象来保存上一次的某一个下,下面保存的是当前的count

 const App = memo( () => {
   const [count, setCount] = useState(0)
   const countRef Ref = useRef()
   // 每次渲染的时候在这里获取最新的值
   countRef.current = count
   // 这里可以不添加依赖,这样传递给子组件不会引发子组件的更新
   const increment = useCallback( () => {
     setCurrent(countRef.current + 1)
   },[])
 })

感谢coderwhy老师的课程,收获良多。