react hooks--useCallback

概述

useCallback缓存的是一个函数,主要用于性能优化!!!

基本用法

如何进行性能的优化呢?

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

语法:

复制代码
const memoizedCallback = useCallback(
  () => {
    doSomething(a, b);
  },
  [a, b],
);
  • 通常使用useCallback的目的是不希望子组件进行多次渲染,并不是为了函数进行缓存;
  • 在使用 React.memo 时,对于对象类型的 props,只会比较引用(浅对比)。
  • 但是,因为组件每次更新都会创建新的 props 值,比如,新的对象、事件处理程序等(函数组件的特性)。
  • 这就导致:React.memo 在处理对象类型的 props 时,会失效(每次的 props 都是新对象)。
  • 但是,我们还是想让 React.memo 在处理对象类型的 props 时,也有效。
  • 为了让 React.memo 处理对象类型的 props 有效,只要在组件更新期间保持对象类型引用相等即可

这时候,就要用到以下两个 Hooks:

  • useCallback Hook:记住函数的引用,在组件每次更新时返回相同引用的函数。
  • useMemo Hook:记住任意数据(数值、对象、函数等),在组件每次更新时返回相同引用的数据【功能之一】

示例:

复制代码
import {useCallback, useState} from "react";

export default function UseCallback() {

    let [firstName, setFirstName] = useState('张');
    let [lastName, setLastName] = useState('三');

    let getFullName = useCallback(() => {
        return firstName + lastName
    }, [firstName, lastName])

    return (
        <div>
            姓名:{getFullName()}
        </div>
    )
}

缓存了一个函数,可以在组件中使用!!!

演示示例

使用场景:在使用 React.memo 时,为了组件每次更新时都能获取到相同引用的函数,就要用到 useCallback Hook

注意:需要配合 React.memo 高阶函数一起使用

作用:记忆传入的回调函数,这个被记住的回调函数会一直生效,直到依赖项发生改变

解释:

  • 第一个参数:必选,需要被记忆的回调函数。

  • 第二个参数:必选,依赖项数组,用于指定回调函数中依赖(用到)的数据(类似于 useEffect 的第二个参数)。

  • 即使没有依赖,也得传入空数组([]),此时,useCallback 记住的回调函数就会一直生效。

  • 返回值:useCallback 记住的回调函数。

  • useCallback 记住的回调函数会一直生效(或者说会一直返回同一个回调函数),直到依赖项发生改变。

    import React, { memo, useState, useCallback, useRef } from 'react'

    const App = memo(() => {
    const [count, setCount] = useState(0)
    const [money, setMoney] = useState(1000)

    复制代码
    // 初始写法
    const help = useCallback(() => {
      setCount(count - 1)
    }, [count])
    
    // 优化写法:useRef--在组件多次渲染时,返回的是同一个值
    
    // 这种写法容易陷入闭包陷阱
    const help = useCallback(() => {
      setCount(count - 1)
    }, [])
    
    // 推荐优化写法:
    const countRef = useRef();
    countRef.current = count;
    const help = useCallback(() => {
      setCount(countRef.current - 1)
    }, [])
    
    return (
      <div>
        <h1>计数器</h1>
        <div>豆豆被打了{count}次</div>
        <div>金钱:{money}</div>
        <button onClick={() => setCount(count + 1)}>打豆豆</button>
        <button onClick={() => setMoney(money + 100)}>加钱</button>
        <hr />
        {count < 5 ? <DouDou count={count} help={help}></DouDou> : '豆豆被打死了'}
      </div>
    )

    })

    export default App

Doudou.jsx

复制代码
// 子组件
const DouDou = memo(({ count, help }) => {
  console.log('豆豆组件渲染')
  return (
    <div>
      <h3>我是豆豆组件{count}</h3>
      <button onClick={help}>续命</button>
    </div>
  )
})
export default Doudou

总结:

要配合 memo 不然可能反而会降低性能

  1. 当需要将一个函数传递给子组件,最好使用 useCallback 进行优化,将优化之后的函数,传递给子组件
  2. 当需要将一个函数传递给子组件时,最好使用useCallback进行优化,将优化之后的函数传递给子组件

尽量不要使用 useCallback

我建议在项目中尽量不要用 useCallback,大部分场景下,不仅没有提升性能,反而让代码可读性变的很差。

useCallback 大部分场景没有提升性能

useCallback 可以记住函数,避免函数重复生成,这样函数在传递给子组件时,可以避免子组件重复渲染,提高性能。

基于以上认知,很多人(包括我自己)在写代码时,只要是个函数,都加个 useCallback,是你么?反正我以前是。

但我们要注意,提高性能还必须有另外一个条件,子组件必须使用了 shouldComponentUpdate 或者 来忽略同样的参数重复渲染。

假如 ExpensiveComponent 组件只是一个普通组件,是没有任何用的。比如下面这样:

必须通过 React.memo 包裹 ExpensiveComponent ,才会避免参数不变的情况下的重复渲染,提高性能。

所以,useCallback 是要和 shouldComponentUpdate/React.memo 配套使用的,你用对了吗?当然,我建议一般项目中不用考虑性能优化的问题,也就是不要使用 useCallback 了,除非有个别非常复杂的组件,单独使用即可。

useCallback 让代码可读性变差

我看到过一些代码,使用 useCallback 后,大概长这样:

在上面的代码中,变量依赖一层一层传递,最终要判断具体哪些变量变化会触发 useEffect 执行,是一件很头疼的事情。

我期望不要用 useCallback,直接裸写函数就好:

在 useEffect 存在延迟调用的场景下,可能造成闭包问题,那通过咱们万能的方法就能解决:

对 useCallback 的建议就一句话:没事别用 useCallback。

相关推荐
萌萌哒草头将军5 小时前
⚡⚡⚡尤雨溪宣布开发 Vite Devtools,这两个很哇塞 🚀 Vite 的插件,你一定要知道!
前端·vue.js·vite
小彭努力中5 小时前
7.Three.js 中 CubeCamera详解与实战示例
开发语言·前端·javascript·vue.js·ecmascript
浪裡遊6 小时前
跨域问题(Cross-Origin Problem)
linux·前端·vue.js·后端·https·sprint
LinDaiuuj6 小时前
判断符号??,?. ,! ,!! ,|| ,&&,?: 意思以及举例
开发语言·前端·javascript
敲厉害的燕宝6 小时前
Pinia——Vue的Store状态管理库
前端·javascript·vue.js
Aphasia3117 小时前
react必备JavaScript知识点(二)——类
前端·javascript
玖玖passion7 小时前
数组转树:数据结构中的经典问题
前端
呼Lu噜7 小时前
WPF-遵循MVVM框架创建图表的显示【保姆级】
前端·后端·wpf
珠峰下的沙砾7 小时前
Vue3 里 CSS 深度作用选择器 :global
前端·javascript·css
航Hang*7 小时前
WEBSTORM前端 —— 第2章:CSS —— 第3节:背景属性与显示模式
前端·css·css3·html5·webstorm