React-useCallback

useCallback

useCallback 是一个允许你在多次渲染中缓存函数的 React Hook。

使用方式

const cachedFn = useCallback(fn, dependencies)

传入的参数为

  • 想要缓存的函数 fn 。此函数可以接受任何参数并且返回任何值。在初次渲染的时候,useCallback 会直接返回这个函数。在后续的渲染中,如果依赖项没有发生变化,那么直接返回前面缓存的函数 fn 。如果依赖项发生变化,那么重新生成并传入函数,并且 React 会将这个新函数缓存起来,以便下次使用。
  • 依赖项 dependencies 。这个依赖项包含了组件中以及函数 fn 中使用的所有响应值列表。响应式值包括 props、state,和所有在你组件内部直接声明的变量和函数。

注意事项:

  • useCallback 是一个 Hook,所以应该在 组件的顶层 或自定义 Hook 中调用。你不应在循环或者条件语句中调用它。

组件中的使用方式

import { useCallback } from "react"

export default function UseCallbackDemo () {
	const callFn = useCallback(() => {
        console.log('callFn执行了')
    })
}

避免组件重新渲染

子组件 Message 用 memo 包装了,也就是当 props 不变的时候,子组件 Message 不会重新渲染。

而且父组件 UseCallbackDemo 传入子组件 Message 的函数 onGetSonMessage 函数主体没有发生变化。

// Messgae.jsx
import { memo } from 'react'

const Message = memo(function Message() {
  console.log('Message组件渲染了')
  return <div>this is Message</div>
})

export default Message
// UseCallbackDemo.jsx
import { useState, useCallback } from "react"
import List from "./components/List"
import Message from "./components/Message"

export default function UseCallbackDemo () {
  console.log('UseCallbackDemo渲染')
  const [count, setCount] = useState(0)

  const handleClick = () => {
    setCount(count + 1)
  }

  const onGetSonMessage = () => {
    console.log('onGetSonMessage')
  }

  return (
    <div>
      <div>count: {count}</div>
      <div>
        <button onClick={handleClick}>add</button>
      </div>
      <div>
        <List></List>
      </div>
      <div>
        <Message onGetSonMessage={onGetSonMessage}></Message>
      </div>
    </div>
  )
}

在这里插入图片描述

但是点击了按钮 add 后,父组件重新渲染之后,子组件 Message 也会跟着重新渲染。这是因为每次渲染函数内部都拥有独立的 props 和 state ,以及独立的变量和函数。在每次渲染时,函数内部都会重新创建函数 onGetSonMessage ,这就导致函数 onGetSonMessage 在每次渲染中的引用地址都不同。另外子组件是通过 Object.is 来判断 props 是否与上一次相同。所以子组件 Message 即便使用了memo来包装,也会重新渲染。为了避免这种情况,我们可以使用 useCallback 来包装函数 onGetSonMessage 。

import { useState, useCallback } from "react"
import List from "./components/List"
import Message from "./components/Message"

export default function UseCallbackDemo () {
  console.log('UseCallbackDemo渲染')
  const [count, setCount] = useState(0)

  const handleClick = () => {
    setCount(count + 1)
  }

  const onGetSonMessage = useCallback(() => {
    console.log('onGetSonMessage')
  }, [])

  return (
    <div>
      <div>count: {count}</div>
      <div>
        <button onClick={handleClick}>add</button>
      </div>
      <div>
        <List></List>
      </div>
      <div>
        <Message onGetSonMessage={onGetSonMessage}></Message>
      </div>
    </div>
  )
}

在这里插入图片描述

防止频繁触发Effect

import { useState, useCallback, useEffect } from "react"
import List from "./components/List"
import Message from "./components/Message"

export default function UseCallbackDemo () {
  console.log('UseCallbackDemo渲染')
  const [count, setCount] = useState(0)
  const [msg, setMsg] = useState('msg')

  const handleClick = () => {
    setCount(count + 1)
  }
  
  const handleInput = (e) => {
    setMsg(e.target.value)
  }

  const onGetSonMessage = useCallback(() => {
    console.log('onGetSonMessage')
  }, [])

  // 防止频繁触发Effect
  const fn = () => {
    return {
      label: 'apple',
      key: 'apple',
      count
    }
  }

  useEffect(() => {
    console.log('useEffect-fn')
    console.log(fn().label)
  }, [fn])

  return (
    <div>
      <div>{msg}-count: {count}</div>
      <div>
        <button onClick={handleClick}>add</button>
      </div>
      <div>
        <input type="text" onChange={handleInput} />
      </div>
      <div>
        <List></List>
      </div>
      <div>
        <Message onGetSonMessage={onGetSonMessage}></Message>
      </div>
    </div>
  )
}

在这里插入图片描述

同样的问题:因为在每次渲染时,函数内部都会重新创建函数 fn ,这就导致函数 fn 在每次渲染中的引用地址都不同。所以可以用 useCallback 来包装函数 fn 。

import { useState, useCallback, useEffect } from "react"
import List from "./components/List"
import Message from "./components/Message"

export default function UseCallbackDemo () {
  ...

  // 防止频繁触发Effect
  const fn = useCallback(() => {
    return {
      label: 'apple',
      key: 'apple',
      count
    }
  }, [count])

  useEffect(() => {
    console.log('useEffect-fn')
    console.log(fn().label)
  }, [fn])

  return (
    <div>
      <div>{msg}-count: {count}</div>
      <div>
        <button onClick={handleClick}>add</button>
      </div>
      <div>
        <input type="text" onChange={handleInput} />
      </div>
      <div>
        <List></List>
      </div>
      <div>
        <Message onGetSonMessage={onGetSonMessage}></Message>
      </div>
    </div>
  )
}

在这里插入图片描述

  • 24
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值