React 第二十六章 Hook useCallback

news2024/11/28 16:47:24

useCallback 是 React 提供的一个 Hook 函数,用于优化性能。它的作用是返回一个记忆化的函数,当依赖发生变化时,才会重新创建并返回新的函数。

在 React 中,当一个组件重新渲染时,所有的函数都会被重新创建。这可能会导致一些问题,特别是在涉及到将函数作为 prop 传递给子组件时,因为每次父组件重新渲染时,子组件都会接收到一个新的函数 prop,从而触发子组件的不必要的重新渲染。

使用 useCallback 可以避免这个问题。它接收两个参数:一个回调函数和一个依赖数组。当依赖数组中的值发生变化时,返回的回调函数会被重新创建,否则会返回之前创建的回调函数。

示例代码

App 根组件,引入了 ChildCom1ChildCom2 这两个子组件:

import { useState } from 'react';
import ChildCom1 from "./components/ChildCom1"
import ChildCom2 from "./components/ChildCom2"

import styles from "./css/App.module.css"

function App() {
  const [counter, setCounter] = useState(0);
  console.log("App渲染了")
  return (
    <div className={styles.container}>
      <div className={styles.btnContainer}>
        <div>{counter}</div>
        <button onClick={() => setCounter(counter + 1)}>+1</button>
      </div>


      <div className={styles.childComContainer}>
        <ChildCom1 />
        <ChildCom2 />
      </div>

    </div>
  );
}

export default App;

ChildCom1 子组件:

import { useState } from "react"
function ChildCom1() {
    const [counter, setCounter] = useState(0);
    console.log("ChildCom1 渲染了")
    return (
        <div style={{
            width: "200px",
            height: "100px",
            border: "1px solid"
        }}>
            ChildCom1
            <div>{counter}</div>
            <button onClick={() => setCounter(counter + 1)}>+1</button>
        </div>
    );
}

export default ChildCom1;

ChildCom2 组件基本相同

此时在我们的应用中,各个组件内部维护了自身的数据,组件内部数据的更新并不会影响到同级组件和祖级组件。效果如下:

iShot_2022-12-02_15.46.37

可以看到,父组件的更新会导致两个子组件更新,这是正常的,子组件各自的更新不会影响其他组件。

但是,倘若两个子组件的数据都来自于父组件,情况就不一样了。

这里我们把上面的代码稍作修改,如下:

App.jsx 根组件,为两个子组件提供了数据以及修改数据的方法

import { useState } from 'react';
import ChildCom1 from "./components/ChildCom1"
import ChildCom2 from "./components/ChildCom2"

import styles from "./css/App.module.css"

function App() {
  const [counter, setCounter] = useState(0);
  const [counter1, setCounter1] = useState(0);
  const [counter2, setCounter2] = useState(0);
  console.log("App渲染了")
  return (
    <div className={styles.container}>
      <div className={styles.btnContainer}>
        <div>{counter}</div>
        <button onClick={() => setCounter(counter + 1)}>+1</button>
      </div>


      <div className={styles.childComContainer}>
        <ChildCom1 counter={counter1} setCounter={setCounter1}/>
        <ChildCom2 counter={counter2} setCounter={setCounter2}/>
      </div>

    </div>
  );
}

export default App;

ChildCom1 子组件接收从父组件传递过来的数据,并调用父组件传递过来的方法修改数据

function ChildCom1(props) {
    console.log("ChildCom1 渲染了")
    return (
        <div style={{
            width: "200px",
            height: "100px",
            border: "1px solid"
        }}>
            ChildCom1
            <div>{props.counter}</div>
            <button onClick={() => props.setCounter(props.counter + 1)}>+1</button>
        </div>
    );
}

export default ChildCom1;

效果如下:

iShot_2022-12-02_15.52.31

可以看到,在更新子组件的数据时,由于数据是从父组件传递下去的,相当于更新了父组件数据,那么父组件就会重新渲染,最终导致的结果就是父组件下面所有的子组件都重新渲染了。

首先,我们会想到使用前面所讲的 React.memo 来解决这个问题,如下:

import React from "react"
function ChildCom1(props) {
    console.log("ChildCom1 渲染了")
    return (
        <div style={{
            width: "200px",
            height: "100px",
            border: "1px solid"
        }}>
            ChildCom1
            <div>{props.counter}</div>
            <button onClick={() => props.setCounter(props.counter + 1)}>+1</button>
        </div>
    );
}

// 相同的 props 传入的时候该组件不需要重新渲染
export default React.memo(ChildCom1);

在上面的代码中,我们使用 React.memo 来缓存 ChildCom1 组件,这样在相同的 props 传入时,该组件不会重新渲染。

但是假设此时 App 组件还有一个单独的函数传入,那就不那么好使了:

function App() {
  // App 组件自身有一个状态
  // ...
  console.log("App 渲染了")

  function test() {
    console.log("test");
  }


  return (
    <div className={styles.container}>
      {/* ... */}

      <div className={styles.childComContainer}>
        <ChildCom1 counter={counter1} setCounter={setCounter1} test={test} />
        <ChildCom2 counter={counter2} setCounter={setCounter2} test={test} />
      </div>
    </div>
  );
}

在上面的代码中,我们还向两个子组件传入了一个 test 函数,由于每次 App 组件的重新渲染会生成新的 test 函数,所以对于两个子组件来讲传入的 test 导致 props 不同所以都会重新渲染。

此时就可以使用 useCallback 来解决这个问题,语法如下:

const memoizedCallback = useCallback(
  () => {
    doSomething(a, b);
  },
  [a, b],
);

把内联回调函数及依赖项数组作为参数传入 useCallback,它将返回该回调函数的 memoized 版本,该回调函数仅在某个依赖项改变时才会更新。

接下来我们来使用 useCallback 优化上面的问题,对 App.jsx 做如下的修改:

import React, { useState, useCallback } from 'react';
import ChildCom1 from "./components/ChildCom1"
import ChildCom2 from "./components/ChildCom2"

import styles from "./css/App.module.css"

function App() {

  const [counter, setCounter] = useState(1); // 这是 App 组件自身维护的状态
  const [counter1, setCounter1] = useState(1); // 这是要传递给 ChildCom1 组件的数据
  const [counter2, setCounter2] = useState(1); // 这是要传递给 ChildCom2 组件的数据

  console.log("App组件渲染了")

  // 每次重新渲染的时候,就会生成一个全新的 test 函数
  // 使用了 useCallback 之后,我们针对 test 函数做了一个缓存  
  const newTest = useCallback(function test(){
    console.log("test触发了")
  },[])

  return (
    <div className={styles.container}>
      {/* 自身的计数器 */}
      <div className={styles.btnContainer}>
        <div>counter:{counter}</div>
        <button onClick={() => setCounter(counter + 1)}>+1</button>
      </div>

      {/* 使用子组件 */}
      <div className={styles.childComContainer}>
        <ChildCom1 counter={counter1} setCounter={setCounter1} test={newTest}/>
        <ChildCom2 counter={counter2} setCounter={setCounter2} test={newTest}/>
      </div>
    </div>
  );
}

export default App;

在上面的代码中,我们对 test 函数做了缓存,从而保证每次传入到子组件的这个 props 和之前是相同的。

注意

  • 记住:useCallback 主要就是对函数进行缓存
  • 使用 useCallback 可以提高性能,避免不必要的重渲染。但需要注意的是,过度使用 useCallback 也可能会导致性能问题,因为每次依赖数组的值发生变化时,都会触发函数的重新创建。因此,需要结合具体的场景和需求来决定是否使用 useCallback

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1659178.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何通过汽车制造供应商协同平台,提高供应链的效率与稳定性?

汽车制造供应商协同是指在汽车制造过程中&#xff0c;整车制造商与其零部件供应商之间建立的一种紧密合作的关系。这种协同关系旨在优化整个供应链的效率&#xff0c;降低成本&#xff0c;提高产品质量&#xff0c;加快创新速度&#xff0c;并最终提升整个汽车产业的竞争力。以…

vue2基础语法01——基础语法使用了解

vue2基础语法01——基础语法使用了解 1. vue模板语法1.1 插值语法1.2 指令语法1.3 简单测试完整代码 2. vue中各选项使用&#xff08;简单说几个&#xff09;2.1 el选项2.2 data选项2.2.1 简述2.2.2 data选项的两种类型2.2.3 注意&#xff08;限制&#xff09;2.2.4 更多了解参…

JAVA快速工具代码集

一、前言 在开发过程中&#xff0c;特别是维护老系统&#xff0c;有时候想使用的工具类却使用不了。又要重新造轮子。所以准备点工具类代码是必须的&#xff0c;无奈之举。 二、JSON数据转换 String content cdfQhOrderResModel.getContent(); List<CdfQH…

Python 数据库操作- sqlite3 模块

Python sqlite3 模块 1. 安装 SQLite3 可使用 sqlite3 模块与 Python 进行集成。sqlite3 模块是由 Gerhard Haring 编写的。它提供了一个与 PEP 249 描述的 DB-API 2.0 规范兼容的 SQL 接口。用户不需要单独安装该模块&#xff0c;因为 Python 2.5.x 以上版本默认自带了该模块…

在uniapp里面使用 mp-html 并且开启 latex 功能

在uniapp里面使用 mp-html 并且开启 latex 功能 默认情况下 mp-html 是不会开启 latex 功能的, 如果需要开启 latex 功能是需要到代码操作拉取代码自行打包的。 这里说一下 mp-html 里面的 latex 功能是由 https://github.com/rojer95/katex-mini 提供的技术实现&#xff0c;…

【Spark】Spark编程体验,RDD转换算子、执行算子操作(六)

Spark编程体验 项目依赖管理 <dependencies><dependency><groupId>org.scala-lang</groupId><artifactId>scala-library</artifactId><version>2.12.10</version></dependency><dependency><groupId>org.ap…

通过 Java 操作 redis -- zset 有序集合基本命令

目录 使用命令 zadd&#xff0c;zrange 使用命令 zcard 使用命令 zrem 使用命令 zscore 使用命令 zrank 关于 redis zset 有序集合类型的相关命令推荐看Redis - Zset 有序集合 要想通过 Java 操作 redis&#xff0c;首先要连接上 redis 服务器&#xff0c;推荐看通过 Jav…

武汉凯迪正大—超声波探伤仪的主要功能

武汉凯迪正大超声波探伤仪的主要功能 高精度定量、定位&#xff0c;满足了较近和较远距离探伤的要求&#xff1b; 近场盲区小&#xff0c;满足了小管径、薄壁管探伤的要求&#xff1b; 自动校准&#xff1a;一键式自动校准&#xff0c;操作非常便捷&#xff0c;自动测试探头的…

基于springboot实现贸易行业crm系统项目【项目源码+论文说明】计算机毕业设计

基于springboot实现贸易行业crm系统演示 摘要 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;管理信息系统的实施在技术上已逐步成熟。本文介绍了基于springboot的贸易行业crm系统的开发全过程。通过分析基于springboot的贸易行业crm系统管理的不足&#xff0c;创建…

element ui的无法关掉的提示弹框

使用element的$alert组件的属性把X去掉和确定按钮和取消按钮去掉&#xff1b; import { MessageBox } from element-ui; MessageBox.alert(AI功能已到期或暂未开启, 友情提示, {showClose: false,showCancelButton: false,showConfirmButton: false }); 如果在router的路由守…

华为ensp中BFD和OSPF联动(原理及配置命令)

作者主页&#xff1a;点击&#xff01; ENSP专栏&#xff1a;点击&#xff01; 创作时间&#xff1a;2024年5月6日20点26分 BFD通常指的是双向转发检测。BFD是一个旨在快速检测通信链路故障的网络协议&#xff0c;提供了低开销、短延迟的链路故障检测机制。它主要用于监测两个…

PCIe协议之-TLP路由基础

✨前言&#xff1a; 在PCI Express (PCIe) 技术中&#xff0c;数据包的路由方式对于确保信息能够高效、准确地传送至目标设备至关重要。PCIe定义了几种路由方式&#xff0c;主要有以下几种。 &#x1f31f;地址路由&#xff08;Address Based Routing&#xff09; 这是最基本…

hypertherm海宝EDGE控制器显示屏工控机维修

海宝工控机维修V3.0/4.0/5.0&#xff1b;hypertherm数控切割机系统MICRO EDGE系统显示屏维修&#xff1b; 美国hypertherm公司mirco edge数控系统技术标准如下&#xff1a; 1&#xff09; p4处理器 2&#xff09; 512mb内存 3&#xff09; 80g硬盘&#xff0c;1.44m内置软驱…

【连连国际注册/登录安全分析报告】

连连国际注册/登录安全分析报告 前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 暴力破解密码&#xff0c;造成用户信息泄露短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨…

解决VSCode中Debug和运行路径不一致

哈喽&#xff0c;大家好&#xff0c;我是木头左&#xff01; 当尝试调试程序时&#xff0c;程序的运行路径与预期不符。这通常会导致程序无法正确读取文件或访问资源&#xff0c;从而影响调试过程。为了解决这个问题&#xff0c;可以在launch.json文件中配置CWD参数&#xff0c…

C语言学习初级阶段

文章目录 一、运算符与表达式1.1、运算符分类1.2、关系运算符与关系表达式2.1、逻辑运算符与逻辑表达式2.2、赋值运算符2.3、求字节运算符 二、选择、循环2.1、选择if-else讲解.2.1.1、关系表达式与逻辑表达式 2.2、while、for、continue、break讲解2.2.1、while循环2.2.2、for…

「PKUSC2018 星际穿越」Solution

题目传送门 [PKUSC2018] 星际穿越 简述题意 给定一张 n n n 个点的无向图和 q q q 次询问&#xff0c;每一个点 i i i 与 ∀ j ∈ [ l i , i − 1 ] \forall j \in [l_i , i -1] ∀j∈[li​,i−1] 都存在一条长度为 1 1 1 的无向边&#xff0c;每次询问给定 l i , r i…

DRF的三大认证

【 零 】一些注意事项 【1】如何使用视图类 #1 APIView -如果后续&#xff0c;写个接口&#xff0c;不需要用序列化类&#xff0c;不跟数据库打交道-发送短信接口&#xff0c;发送邮件接口#2 GenericAPIView-如果后续&#xff0c;写个接口&#xff0c;要做序列化或跟数据库打交…

数据结构复习指导之线索二叉树

文章目录 二叉树 考纲内容 复习提示 1.线索二叉树 1.1线索二叉树的基本概念 1.2中序线索二叉树的构造 1.3中序线索二叉树的遍历 1.4先序线索二叉树和后序线索二叉树 知识回顾 二叉树 考纲内容 &#xff08;一&#xff09;树的基本概念 &#xff08;二&#xff09;二叉…

酷柚易汛ERP源码部署/售后更新/搭建/上线维护

一款基于FastAdminThinkPHPLayui开发的ERP管理系统&#xff0c;帮助中小企业实现ERP管理规范化&#xff0c;此系统能为你解决五大方面的经营问题&#xff1a;1.采购管理 2.销售管理 3.仓库管理 4.资金管理 5.生产管理&#xff0c;适用于&#xff1a;服装鞋帽、化妆品、机械机电…