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。

相关推荐
AlienZHOU6 小时前
AI Coding 时代下,我的技术面试实践分享
前端·后端·面试
刺客码8 小时前
Layui 表格固定列行高错位问题解决方案
前端框架·layui·jquery·web
Captaincc9 小时前
AI用量v0.1.11更新发布 新增 jusage doctor 诊断指令 托盘展示token 和余额 新增 AutoClaw 支持
前端·后端·vibecoding
计算机魔术师10 小时前
德国Wiki被黑后两周,OpenAI终于把模型失控的账本摊开了
前端
kyriewen11 小时前
我让 AI 当面试官面了我一轮:第 3 个追问我就卡住了(附 10 道追问清单)
前端·面试·ai编程
IT_陈寒11 小时前
Python的GIL把我坑惨了,多线程跑得比单线程还慢
前端·人工智能·后端
前端snow11 小时前
ai agent --- 多agent框架之图编排引擎-langgraph
前端
竹林81811 小时前
OmniPic Studio v3.2.1 核心技术架构与全平台发版解析文档
前端·浏览器
JamesZhang8007811 小时前
页面内存只涨不跌? 一次泄漏排查, 牵出 WeakMap 的诞生
前端
Z小明12 小时前
第 6 章 组件进阶
前端·vue.js