hooks 系列五:useCallback

1,663 阅读3分钟

快来加入我们吧!

"小和山的菜鸟们",为前端开发者提供技术相关资讯以及系列基础文章。为更好的用户体验,请您移至我们官网小和山的菜鸟们 ( xhs-rookies.com/ ) 进行学习,及时获取最新文章。

"Code tailor" ,如果您对我们文章感兴趣、或是想提一些建议,微信关注 “小和山的菜鸟们” 公众号,与我们取的联系,您也可以在微信上观看我们的文章。每一个建议或是赞同都是对我们极大的鼓励!

前言

这篇文章,我们主要目的是了解一下 (useCallback) 的使用.

useCallback

定义

useCallback 返回一个 memoized 回调函数。

const memoizedCallback = useCallback(fn, deps)

useCallback 所需参数

  • fn :一个函数最终会返回该回调函数,该回调函数仅仅只在 deps 参数发生改变时才会更新。
  • deps : 用于触发 fn 回调函数改变的参数列表。

注意: deps 是一个数组,也就是说改变 fn 的参数可以有很多。

把内联回调函数及依赖项数组作为参数传入 useCallback,它将返回该回调函数的 memoized 版本,该回调函数仅在某个依赖项改变时才会更新。当你把回调函数传递给经过优化的并使用引用相等性去避免非必要渲染(例如 shouldComponentUpdate)的子组件时,它将非常有用。

如何使用

我们先来看一个简单的例子。

这个是一个子组件,接受父类的一个 fn 方法并展示其按钮。

const ChildComponent = memo(function (props) {
  console.log('child render!')
  return <Button onClick={props.fn}> showTime</Button>
})

注意: 这里的 memo 也是一个 Hooks , 详情请见Hook API 索引 – React (reactjs.org)

这是一个父组件,里面有一个计数器,一个数字增加按钮,和这个子组件。

function Main() {
  const [count, setcount] = useState(0)

  const ShowTime = () => {
    console.log('now time :' + new Date())
  }

  return (
    <Row
      style={{
        'flex-direction': 'column',
      }}
    >
      <Col>
        <Title>index:{count}</Title>
      </Col>
      <Col>
        <Button onClick={() => setcount(count + 1)}>increase</Button>
      </Col>
      <Col>
        <ChildComponent fn={ShowTime} />
      </Col>
    </Row>
  )
}

我们可以看到,在我们点击 increase 按钮的时候,count 发生了增加,这是正常且合理的。

60cv4-43wz0.gif

但是这个时候我们打开我们的浏览器控制台,就会发现子组件 ChildComponent 在不停的 render

这是不合理的,对于我们来说,子组件应该只受 childname 该参数的影响,如果该参数函数 fn 没发生改变,我们就不应该去 render

注意: 这个地方子组件不停 render 的原因在于,这个 ShowTime 方法在不停的重新创建,然后导致传给子组件的 props 其实是不一样的,因此导致不停 render

这个时候我们就用到了 useCallback

function Main() {
  const [count, setcount] = useState(0)

  const useMemoryCallback = useCallback(() => {
    console.log('now time :' + new Date())
  }, [])

  return (
    <Row
      style={{
        'flex-direction': 'column',
        marginLeft: '10px',
      }}
    >
      <Col>
        <Title>index:{count}</Title>
      </Col>
      <Col>
        <Button onClick={() => setcount(count + 1)}>increase</Button>
      </Col>
      <Col>
        <ChildComponent fn={useMemoryCallback} />
      </Col>
    </Row>
  )
}

这个时候,我们将这个函数放在 useCallback 中返回,因为 deps 参数为空,所以并没有需要改变的,这样子我们在点击 increase 按钮的时候就不会触发子组件的 render 了。

如何检查

依赖项数组(deps)不会作为参数传给回调函数。虽然从概念上来说它表现为:所有回调函数中引用的值都应该出现在依赖项数组中。未来编译器会更加智能,届时自动创建数组将成为可能。

我们推荐启用 eslint-plugin-react-hooks 中的 exhaustive-deps 规则。此规则会在添加错误依赖时发出警告并给出修复建议。

小结

  • useCallback 给我们带来了记忆函数,结合子组件和 useMemo 可以达到优化组件加载的效果。
  • 如果子组件接受了一个方法作为属性,我们在使用 React.memo 这种避免子组件做没必要的渲染时候,就需要用 useCallback 进行配合,否则 React.memo 将无意义。

下节预告

在下节中,我们将为大家介绍 hook 规则 ,敬请期待!