React useCallback如何使其性能最大化?

news2024/9/23 9:30:49

前言

React中最让人畅谈的就是其带来的灵活性,可以说写起来非常的舒服。但是也就是它的灵活性太强,往往让我们忽略了很多细节的地方,而就是这些细节的东西能进行优化,减小我们的性能开销。可以说刚学React和工作几年后写React的代码真的是完全不一样。
废话不多说了,今天带大家来了解useCallback有哪些细节之处可以让我们做进一步的性能优化。
如果你不太了解useCallback,可以看看我这篇文章正确使用useCallback的姿势

没做优化前的代码

简单的一个结构,父子组件,子组件接收父组件触发数字改变的函数。

父组件

import React, { memo, useState } from "react";
import Son from "../commponet/Son";

const Optimize = memo(() => {
  console.log("Optimize 组件重新渲染了");
  const [count, setcount] = useState(10);
  const [isshow, setisshow] = useState(true);

  //   未优化
  const addNum = () => {
    setcount(count + 10);
  };
  const changeShow = () => {
    setisshow(!isshow);
  };


  return (
    <div>
      <h2>Optimize</h2>
      <h3>
        count:{count}---isshow:{isshow.toString()}
      </h3>
      <button onClick={(e) => addNum()}>add+10</button>
      <button onClick={(e) => changeShow()}>changeshow</button>
      <Son addNum={addNum} />
    </div>
  );
});

export default Optimize;

子组件

import React, { memo } from "react";

interface Props {
  count?: number;
  addNum: Function;
}

const Son = memo((props: Props) => {
  const {  addNum } = props;
  console.log("Son 组件重新渲染了");
  return (
    <div>
      <h2>Son</h2>
      <button onClick={(e) => addNum()}>click</button>
    </div>
  );
});

export default Son;

测试—结论

三个按钮都点击一遍,发现

image.png
原因不用多说了吧,看过我之前那篇文章的都懂,下面进行进一步优化。

第一次优化

使用useCallback来进行优化
父组件只贴一些关键代码
子组件代码未改变,这里就不贴了

//   第一次优化
  const addNum = useCallback(() => {
    setcount(count + 10);
  }, [count]);
  const changeShow = useCallback(() => {
      setisshow(!isshow);
    }, [isshow]);

测试—结论

前两次点击都是触发 addNum 最后一次点击触发 changeShow

image.png

一般优化都只是到此为止,可是我们想想,子组件中并未用到我们的count 只是接收了addNum函数,如果我们能只让父组件进行更新子组件不更新不才是最理想的状态吗?

最终优化

首先要搞明白为什么子组件会更新?那是因为每次count更新时,重新生成一个新函数,引用地址发生了变化,子组件发现前后;两次地址不一样所以进行了更新。那能不能让其始终用最开始的函数地址呢?

  const changeShow = useCallback(() => {
    setisshow(!isshow);
  }, [isshow]);

  // 第二次优化
  const countRef = useRef(count);
  countRef.current = count;
  const addNum = useCallback(() => {
    setcount(countRef.current + 10);
  }, []);

使用空数组代表我们一直不会改变函数地址,用的是最开始的那个函数
那如何修改值呢?这里巧妙用useRef来帮助我们记录每次更新的值
为什么使用useRef? 因为useRef是贯穿整个生命周期的,每次render重新执行时useRef都是同一个对象。不信?那我们测试测试呗。

import React, { memo, useCallback, useRef, useState } from "react";
import Son from "../commponet/Son";

let obj: any = null;
const Optimize = memo(() => {
  const aaa = useRef();
  console.log(obj === aaa);
  obj = aaa;
  console.log("Optimize 组件重新渲染了");
  const [count, setcount] = useState(10);
  const [isshow, setisshow] = useState(true);

    //   未优化
     const addNum = () => {
       setcount(count + 10);
     };
     const changeShow = () => {
       setisshow(!isshow);
    };


  return (
    <div>
      <h2>Optimize</h2>
      <h3>
        count:{count}---isshow:{isshow.toString()}
      </h3>
      <button onClick={(e) => addNum()}>add+10</button>
      <button onClick={(e) => changeShow()}>changeshow</button>
    </div>
  );
});

export default Optimize;

image.png
可以看到确实是同一个对象的,如果我们采用之前的count 或者自己定义一个新对象就会陷入闭包陷阱了!
它们都会保存最开始的值的引用,后续render更新的是新地址,不会被其引用,感兴趣的可以去测试测试,这里就不测试了。

  // 用普通对象和之前值是否可行?闭包陷阱
    const countRef = { current: count };
    countRef.current = count;
    const addNum = useCallback(() => {
      // setcount(countRef.current + 10);
      setcount(count + 10);
    }, []);

测试—结论

三个按钮都点击了一次,发现都只是父组件更新了

image.png

总结

这里并不是说所有的 useCallback 都要进行这样处理,其实是没必要的。我们要根据实际情况来分析,就比如我们上面这个例子那是建立在子组件没有依赖父组件的count数据,所以我们可以选择只渲染父组件就可以,若是子组件也用到了count数据则推荐使用第一次优化即可。
一键三连,关注不迷路!

QQ图片20200210181218.jpg

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

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

相关文章

JVM内存结构之堆(重要)

1、概述每个JVM进程有且只有一个堆&#xff0c;进程内的所有线程共享这块区域&#xff0c;堆区在JVM启动的时候即被创建&#xff0c;其空间大小也就确定了&#xff0c;是JVM内存结构中最大的一块区域。由于线程共享&#xff0c;堆也就成了JVM内存管理的核心区域。《Java虚拟机规…

热爱所有热爱

想成为这样的一个人&#xff0c;在工作中是一名充满极客精神的Programmer&#xff0c;处理遇到的问题能够游刃有余&#xff0c;能够做出优雅的设计&#xff0c;写出一手优秀的代码&#xff0c;还有着充分的学习能力和业务能力&#xff0c;做一名职场中的佼佼者。 在工作之余还能…

15、条件概率、全概率公式、贝叶斯公式、马尔科夫链

条件概率定义&#xff1a;设A、B是两个事件&#xff0c;且&#xff0c;P(A) > 0 则称 为事件A发生的条件下事件B的条件概率对这个式子进行变形&#xff0c;即可得到概率的乘法公式&#xff1a;P(A) > 0 时&#xff0c;则P(B) > 0 时&#xff0c;则乍一看&#xff0c;…

数字化转型导师坚鹏:BLM农商行数字化转型实战解决方案及案例

BLM农商行数字化转型实战解决方案及案例研究 ——以BLM模型为核心&#xff0c;践行知行合一思想&#xff0c;实现知行果合一 课程背景&#xff1a; 很多农商行存在以下问题&#xff1a; 不知道如何开展数字化转型工作&#xff1f; 不清楚农商行数字化转型方法论&#xff1f; …

Python—单分支结构

&#xff08;1&#xff09;if分支语句 Python中if语句的语法结构&#xff1a; if <条件表达式>&#xff1a;    满足条件运行的代码1    满足条件运行的代码2 代码示例&#xff1a; age 12 if age > 18:print(去上网)if 1 1 2 and :print(我满足条件了)if 1 …

【C++】C++入门(下)

引用 什么是引用&#xff1f;   引用是给一个已经存在的变量取一个别名&#xff0c;在语法上并不会给这个别名开一个空间&#xff0c;它和她引用的变量共用一个空间。但是实际上引用也是开了一块空间的&#xff0c;用来存放引用名。引用是按照指针的方式来实现的。引用语法&…

电子技术——B类输出阶

电子技术——B类输出阶 下图展示了一个B类输出阶的原理图&#xff0c;B类输出阶由两个互补的BJT组成&#xff0c;不同时导通。 原理 当输入电压 vI0v_I 0vI​0 的时候&#xff0c;两个晶体管都截止输出电压为零。当 vIv_IvI​ 上升至超过0.5V的时候&#xff0c;此时 QNQ_NQN…

MVVM 架构进阶:MVI 架构详解

前言Android开发发展到今天已经相当成熟了&#xff0c;各种架构大家也都耳熟能详&#xff0c;如MVC,MVP,MVVM等&#xff0c;其中MVVM更是被官方推荐&#xff0c;成为Android开发中的显学。不过软件开发中没有银弹&#xff0c;MVVM架构也不是尽善尽美的&#xff0c;在使用过程中…

【软件测试】从功能到自动化测试,测试人的进阶之路细节,这些必不可少......

目录&#xff1a;导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09;前言 测试流程&#xff0…

SQL语法2

SQL语法视图view常用函数三张重要的表接SQL语法第一篇文章&#xff0c;接下来所有的操作都基于safe这张表进行。视图view 语法&#xff1a;create view 视图名称 as select 原表中的列名 from 原表名&#xff1b; 删除视图&#xff1a; 语法&#xff1a;drop view 视图名&…

【Python】Numpy数组的切片、索引详解:取数组的特定行列

【Python】Numpy数组的切片、索引详解&#xff1a;取数组的特定行列 文章目录【Python】Numpy数组的切片、索引详解&#xff1a;取数组的特定行列1. 介绍2. 切片索引2.1 切片索引先验知识2.1 一维数组的切片索引2.3 多维数组的切片索引3. 数组索引&#xff08;副本&#xff09;…

Linux解压压缩

打包tar首先我们得提一下专门用于打包文件的命令——tartar用于备份文件&#xff0c;打包多个文件或者目录&#xff0c;也可以用于还原被打包的文件假设打包目录test下的文件 tar -cvf test.tar ./test 假设打包目录test下的文件,并用gzip命令将包压缩 tar -zcvf test.tar ./te…

华为OD机试题,用 Java 解【蛇形矩阵】问题

最近更新的博客 华为OD机试 - 猴子爬山 | 机试题算法思路 【2023】华为OD机试 - 分糖果(Java) | 机试题算法思路 【2023】华为OD机试 - 非严格递增连续数字序列 | 机试题算法思路 【2023】华为OD机试 - 消消乐游戏(Java) | 机试题算法思路 【2023】华为OD机试 - 组成最大数…

22河北省赛 F

Problem - F - Codeforces 思维构造题 题意: 一个长为n的直线洞窟中, 每一个1~n的整数位置都有一个洞口可以容纳一只兔子 有一只兔子在这个洞窟里四处躲藏, 每一回合都必须向左或者向右移动, 不能待在原地 你每一次都可以在兔子移动之前检查任何一个洞口 要求在最坏的情况…

银行数字化转型导师坚鹏:平安银行数字化转型—橙E网战略研究

平安银行对公业务数字化转型案例—橙E网战略研究课程背景&#xff1a; 很多银行存在以下问题&#xff1a; 不清楚银行对公业务数字化转型能否成功&#xff1f; 不知道其它银行对公业务数字化转型的实际做法&#xff1f; 课程特色&#xff1a; 用实战案例解读平安银行对公业务…

python网络数据获取

文章目录1网络爬虫2网络爬虫的类型2.1通用网络爬虫2.1.12.1.22.2聚焦网络爬虫2.2.1 基于内容评价的爬行策略2.2.2 基于链接结构的爬行策略2.2.3基于增强学习的爬行策略2.2.4基于语境图的爬行策略2.3增量式网络爬虫深层网页爬虫3网络爬虫基本架构3.1URL管理模块3.2网页下载模块3…

算法进阶-动态规划

经典例题 大家肯定想用递归做 思路大概就是这样 递归到最后一行就是对应的D(i,j) 然后往上推 但是这样会超时&#xff0c;因为存在大量的重复计算 比如调用第一行MasSum(7)需要调用MaxSum(3)和MaxSum(8) 但是调用第二行MaxSum(3)还要调用3行的MaxSum(8)和3行的MaxSum(1) 第二行…

English Learning - L2-2 英音地道语音语调 2023.02.23 周四

English Learning - L2-2 英音地道语音语调 2023.02.23 周四查音标的工具怎么练习效果好准备工作大小声练习大元音开口度的对比舌位对比复习后元音 /ɑː/ /ɔː/ /uː//ɑː//ɔː//uː/前元音 /iː/发音技巧对应单词的发音对应句子的发音常见的字母组合中元音 /ɜː/发音技巧…

作业2.25----通过操作Cortex-A7核,串口输入相应的命令,控制LED灯进行工作

1.通过操作Cortex-A7核&#xff0c;串口输入相应的命令&#xff0c;控制LED灯进行工作 例如在串口输入led1on,开饭led1灯点亮 2.例如在串口输入led1off,开饭led1灯熄灭 3.例如在串口输入led2on,开饭led2灯点亮 4.例如在串口输入led2off,开饭led2灯熄灭 5.例如在串口输入led…

golangの并发编程(GMP模型)

GMP模型 && channel1. 前言2. GMP模型2.1. 基本概念2.2. 调度器策略2.3. go指令的调度流程2.4. go启动周期的M0和G02.5. GMP可视化2.6. GMP的几种调度场景3. channel3.1. channel的基本使用3.2. 同步器1. 前言 Go中的并发是函数相互独立运行的体现&#xff0c;Gorouti…