React useCallback 和 useMemo 的部分应用场景

2021-10-22

react 16.8 版本引入 Hook 特性,可以让开发者在不编写 class 组件的情况下,在函数式组件内部使用 state 及其它特性;函数式组件也不再是单单的无状态组件,可以进行副作用等操作。Hook 给予开发者抽取组件复用状态逻辑多了一种方式。

Hook 的引进是令人兴奋的,同时也给予了习惯使用生命周期的部分开发者(菜鸟正是在下)一定的心智负担;下面是个人对 useCallbackuseMemo 两个 Hook 的简单了解。

useCallback

官方文档

  • 接收两个参数;第一个参数为回调函数,第二个参数为依赖项数组,返回一个被缓存过的函数;
  • 依赖项数组,控制回调函数是否重新更新的关键;数组元素一般为回调函数的形参;
  • 把缓存过的函数传递于使用引用相等性的子组件时,避免不必要的渲染。
function App() {
  const [count, updateCount] = useState(0)

  const handleChild = useCallback(() => {
    //TODO: do something
      doSomething(a, b)
  }, [a, b])

  return (
    <div className="App">
      <button onClick={() => updateCount(count + 1)}>updateCount</button>
      <p>{count}</p>
      <Children do={handleChild} />
    </div>
  )
}

// memo 实现了函数式组件中的 shouldCompoentUpdate
export default memo(function Children () {
  console.log('re render')
  return <div>children</div>
})

子组件在使用 memo 包裹的情况下,父组件使用 useCallback 用于控制子组件不必要的渲染。

useMemo

官方文档

  • 接收两个参数;第一个参数为回调函数(需要返回值),第二个参数为依赖项数组,返回一个被缓存过的值;
  • 避免组件在每次渲染时都进行高开销计算的优化的策略。
function App() {
  const [count, updateCount] = useState(0)
  const [num, updateNum] = useState(0)

  function expensive (count) {
    // 模拟高开销计算
    console.log('render')
    const array = new Array(count).fill(count);
    return array.reduce((currentTotal, item) => {
      return currentTotal + item;
    }, 0)
  }

  const count_memoized = useMemo(() => expensive(count), [count])
  // const _count = expensive(count)

  return (
    <div className="App">
      <button onClick={() => updateCount(count + 1)}>updateCount</button>
      <button onClick={() => updateNum(num + Math.random())}>updateNum</button>
      <p>{count_memoized}</p>
    </div>
  )
}

未使用 useMemo 时,每次调用 updateNum 会导致 expensive 函数的调用;使用 useMemo 后,当依赖项 count 改变时,才会去调用 expensive 函数。

cd ..