react hook之useMemo

2023-11-13

useMemo的作用

Pass a “create” function and an array of dependencies. useMemo will
only recompute the memoized value when one of the dependencies has
changed.

传递一个创建函数和依赖项,创建函数需要一个返回值,只有在依赖项改变的时候才重新调用此函数,返回一个新的值。
useMemo 也能针对传入子组件的值进行缓存优化
简而言之,useMemo是用来缓存计算属性的。

那么什么是计算属性?

计算属性其实就是函数的返回值,或者说那些需要一个返回值的函数。
在函数中,有些需要我们手动点击去完成一些动作的触发,比如点击打开弹窗,点击按钮变色等等,而有些函数则是直接在渲染的时候就执行了,在dom区域被当做属性值一样去使用,比如计算1+1将这个计算结果展示在界面上,这就是计算属性。计算属性最后一定会用一个return返回一个值

const TestComponent = () => {
    const [count1,setCount1] = useState(0);
    const [count2,setCount2] = useState(0);
    
    //这种是需要我们手动去调用的函数
    const handleFun1 = () => {
        console.log("我需要手动调用,你不点击我不执行");
        setCount1(count + 1);
    } 
    
    //这种被称为计算属性,不需要手动调用,在渲染阶段就会执行的。
    const computedFun2 = () => {
        console.log('我又执行计算了');
        return count2;
    }
    
    return <div onClick = {handleFun1}>
        //每次重新渲染的时候我就会执行
        <span>计算结果: {computedFun2()}</span>
    </div>
}

在上面的代码示例中,computedFun2函数就是一个计算属性。而handleFun1则是一个普通函数。

看上面的例子,组件每次点击执行handleFun1的时候,因为组件内部状态(count1)的改变会让该组件重新渲染而每次组件重新渲染都会让我们去执行computedFun2函数(计算属性),尽管computedFun2函数中只使用到了count2状态,与被改变的状态并没有任何关系。
如果computedFun2函数里面的计算过程非常的复杂,那么每次重新计算无疑的非常麻烦的。而且在我们上面的例子中,我们返回的值并不会因为count1的改变而产生变化。

那么,我们要如何让组件在改变与计算属性无关的状态的时候进行的渲染不触发我们计算属性的重新计算呢?
useMemo就是解决这个问题的。useMemo有两个入参,第一个值填写我们需要缓存的计算属性,第二个值填写依赖,像useCaallback一样,useMemo会在每次需要重新计算的时候去比较依赖是否被更改,只有当依赖改变了被useMemo保护的函数才会重新执行,返回新的值,否则拒绝重新执行,直接返回旧的计算属性值。
将上面HandleFun2方法改变成如下:

const computedFun2 = useMemo(() => {
        console.log('我又执行计算了');
        return count2;
    },[count2])

原来的computedFun2函数和新的computedFun2函数的区别就是否被useMemo包裹。
使用useMemo包裹了computedFun2这个函数之后,computedFun2函数只会在组件初始化的时候和依赖项状态改变(count2)的时候执行,然后不论count2这个状态如何的改变,computedFun2函数都不会再去执行

useMemo使用场景

useMemo不是用得越多越好

useMemo的作用就是缓存,但是缓存都需要成本,所以useMemo这类钩子并不是用得越多越好!大量的使用反而可能导致反向优化。
所有被useMemo保护的函数都会被加入useMemo的工作队列,在组件进行渲染并且此组件内使用了useMemo之后,为了校验改组件内被useMemo包裹的这个计算属性是否需要重新计算,有两个步骤需要做:

  • 先去useMemo的工作队列中找到这个函数
  • 校验这个函数都依赖是否被更改
    这两个步骤都需要成本。当我们大量的使用useMemo之后,非但不能给项目带来性能上的优化,反而会为项目增加负担。

何时使用?

需要复杂计算的时候

比如只是简单那的计算变量的加减乘除之类的,就没必要使用useMemo,而如果是循环处理等情况则使用useMemo才是正向的。比如:
//这种就是完全没必要被useMemo缓存的,计算过程一共也就一个创建变量,一个加一,缓存它反而亏本

// 这个方法只是进行变量加1
const computedFun1 = () => {
    let number = 0;
    number = numebr +1;
    return number;
}

//这个就需要缓存一下了,毕竟他每次计算的计算量还是蛮大的。
//缓存起来,当需要它执行的时候才执行,避免每次渲染都执行。
const computedFun2 = () => {
    let number =  0;
    for(let i=0;i<100000;++i){
        number = number +i-(number-i*1.1);
    }
    return number;
}
当子组件依赖父组件的某一个依赖计算属性并且子组件使用了React.memo进行优化了的时候

React.memo()是通过校验props中的数据是否改变来决定组件是否需要重新渲染的一种缓存技术,也就是说React.memo()其实是通过校验Props中的数据的内存地址是否改变来决定组件是否重新渲染组件的一种技术。
假设我们往子组件传入一个计算属性,当父组件的其他State(与子组件无关的state)改变的时候。那么,因为状态的改变,父组件需要重新渲染,那被React.memo保护的子组件是否会被重新构建?

import {useMemo,memo} from 'react';
const Parent = () => {
    const [parentState,setParentState] = useState(0);  //父组件的state
    
    //需要传入子组件的函数
    const toChildComputed = () => {
        console.log("需要传入子组件的计算属性");
        return 1000;
    }
    
    return (<div>
          <Button onClick={() => setParentState(val => val+1)}>
              点击我改变父组件中与Child组件无关的state
          </Button>
          //将父组件的函数传入子组件
          <Child computedParams={toChildComputed()}></Child>
    <div>)
}

/**被memo保护的子组件**/
const Child = memo(() => {
    consolo.log("我被打印了就说明子组件重新构建了")
    return <div><div>
})

React.memo检测的是props中数据的栈地址是否改变。而父组件重新构建的时候,如果不缓存计算属性,那么计算属性将会被重新计算执行,并返回一个新的值(这意味这返回了一个新的存储地址),新的计算属性传入到子组件中被props检测到栈地址更新。也就引发了子组件的重新渲染。
所以,在上面的代码示例里面,子组件是要被重新渲染的

将toChildComputed改一下:

const toChildComputed = useMemo(() => {
       console.log("需要传入子组件的计算属性");
       return 1000;
    },[])

这样toChildComputed用useMemo包裹,useMemo会在发现依赖没有变化之后返回旧的计算属性值这样的话,父组件重新渲染,子组件中的函数就会因为被useMemo保护而返回旧的计算属性值,子组件就不会检测成地址变化,也就不会重选渲染了。

Tips

事实上在使用中 useMemo 的场景远比 useCallback 要广泛的很多,我们可以将 useMemo 的返回值定义为返回一个函数这样就可以变通的实现了 useCallback。在开发中当我们有部分变量改变时会影响到多个地方的更新那我们就可以返回一个对象或者数组,通过解构赋值的方式来实现同时对多个数据的缓存

const [age, followUser] = useMemo(() => {
  return [
    new Date().getFullYear() - userInfo.birth, // 根据生日计算年龄
    async () => { // 关注用户
      await request('/follow', { uid: userInfo.id });
      // ...
    }
  ];
}, [userInfo]);

return (
  <div>
    <span>name: {userInfo.name}</span>
    <span>age: {age}</span>
    <Card followUser={followUser}/>
    {
      useMemo(() => (
        // 如果 Card1 组件内部没有使用 React.memo 函数,那还可以通过这种方式在父组件减少子组件的渲染
        <Card1 followUser={followUser}/>
      ), [followUser])
    }
  </div>
)

与useCallback相比:
useCallback 与 useMemo 一个缓存的是函数,一个缓存的是函数的返回值。useCallback 是来优化子组件的,防止子组件的重复渲染。useMemo 可以优化当前组件也可以优化子组件,优化当前组件主要是通过 memoize 来将一些复杂的计算逻辑进行缓存。当然如果只是进行一些简单的计算也没必要使用 useMemo,这里可以考虑一些计算的性能消耗和比较 inputs 的性能消耗来做一个权衡。

总结

  • useMemo是用来缓存计算属性的,它会在发现依赖未发生改变的情况下返回旧的计算属性值的地址。
  • useMemo绝不是用的越多越好,缓存这项技术本身也需要成本。
  • useMemo的使用场景之一是:只需要给拥有巨大计算量的计算属性缓存即可。
  • useMemo的另一个使用场景是:当有计算属性被传入子组件,并且子组件使用了react.memo进行了缓存的时候,为了避免子组件不必要的渲染时使用
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

react hook之useMemo 的相关文章

  • 是否可以在没有 Javascript(仅 CSS)的情况下执行相同的操作(悬停效果)?

    我正在尝试创建一个带有图标的按钮像这样 http jsfiddle net pRdMc HTML div div class icon div span Send Email span div CSS button width 270px
  • 使用 获取用于 javascript 的 RSA 密钥?

    我的 Web 项目需要一个 RSA 密钥对 虽然有一些库 但我认为依靠浏览器 为了安全性和速度 为我生成密钥是个好主意 是否可以使用注册机或其他浏览器 API 来执行此操作 我不知道如何从注册机获取密钥 它们似乎是在提交时生成的 但我不想将
  • Google reCaptcha 永远加载

    我在我的网站上使用 Google 的 reCaptcha 2 0 它曾经运行良好 但自从我向公众开放我的网站并获得了更多用户后 recaptcha 不再适用于大多数用户 它加载得很好 但一旦用户单击 我不是机器人 复选框 它会永远加载并且从
  • 为什么“事件”在 Chrome 中全局可用,而在 Firefox 中则不然?

    在回答另一个问题时 出现了一个与event对象在匿名函数中可用 无需传入 在 Chrome 中 下面的代码工作正常 但 Firefox 会抛出错误 document ready function uspsSideboxTrackingClo
  • 如果浏览器在 asp .net 中关闭,请从浏览器中注销?

    我的要求有点复杂 用户正在使用 Web 浏览器访问数据库 而在访问数据库时 如果用户关闭活动页面而不是注销会话 该会话需要自动注销 有人可以指导我如何做这个吗 我在母版页中使用了jquery onbeforeunload 我收到消息离开页面
  • ngx-DataTable 对列进行排序无法正常工作 Angular 4

    虽然我对角度非常陌生 但我在使用 ngx DataTable 时遇到了一些困难 我使用简单的 ngx DataTable 进行简单的操作 问题出在列上 尽管我已将 attr 声明为 sortable true 但排序不起作用 这是代码 表定
  • 如何为 HTML5 音频元素制作加载栏?

    我正在尝试为 HTML5 音频元素制作一个加载栏 显示加载 缓冲的百分比 对于视频标签 可以使用以下方法进行计算 video buffered end 0 video duration 但我无法让它与音频标签一起使用 它只是返回一个固定值
  • jQuery 验证日期范围问题

    我的代码中有很多地方有成对的相关开始和结束日期字段 范围 我需要验证开始日期早于结束日期 我正在使用 jQuery 验证插件 这是我的代码 http jsfiddle net jinglesthula dESz2 http jsfiddle
  • 光滑的旋转木马不工作

    我一直在尝试简单地实现 Slick Carousel 的工作 我已按照 Git 页面上的说明进行操作 https github com kenwheeler slick https github com kenwheeler slick 这
  • Typescript:如何在自定义过滤器中使用角度 $filter

    如何在自定义过滤器中使用 Angular filter 如何注入 filter依赖 module Filters export class CustomFilter public static Factory return function
  • 在 IE10 中禁用捏合放大

    在 IE10 触摸模式下 我希望仅使页面的特定部分可缩放 其余的不应该 我找到了这个 http msdn microsoft com en US library ie hh772044 aspx http msdn microsoft co
  • 单击输入字段会触发窗口调整大小

    我有一个带有徽标 菜单和搜索的标题 当我在桌面上时 我会按该顺序显示所有元素 但如果我的窗口宽度小于 980 像素 菜单会隐藏 有一个切换按钮 并且徽标会与nav并附在徽标之后 如果宽度更大 则徽标将再次分离并附加到 DOM 中的旧位置 w
  • 将 Google 电子表格解析为 Javascript 数组

    我有一个 Google 电子表格 https docs google com spreadsheets d e 2PACX 1vRc8Lx0N wf3f1xAAXkNFUqQjaWPFcde3YjK02gCBqGpUrULwHC6NC0sn
  • AngularJS 输入字段未从控制器内的 setTimeout 更新

    我正在使用 AngularJS 支持的页面 并且我需要在只读输入文本字段内显示正在运行的时钟 与data ng model 为了模拟运行的时钟 我使用了 JavaScript 调度程序setTimeout每 1000 毫秒调用一个函数 该函
  • 在 HTML5 中将两个图像合并到一个画布上

    我正在使用 HTML5 canvas 元素 假设我有 2 个 ImageData 对象 我想将它们组合起来放在一张画布上 假设我不关心这些图像如何组合 两个 ImageData 对象具有完全相同的像素数和形状 组合两个图像的最佳方式是什么
  • 浏览器默认区域设置 - Intl.DateTimeFormat 与 navigator.language

    在对网站进行编码并格式化日期时 我想使用用户在浏览器中设置的区域设置 例如 如果用户定制了他们的chrome settings languages在 Chrome 中设置为非默认值 这就是我想要使用的值 但是 当我在此类浏览器的控制台中运行
  • 父指令属性更改时子指令不会更新

    这是对这两个问题的后续 在父指令和子指令之间传递参数 https stackoverflow com questions 42814530 pass argument between parent and child directives
  • JavaScript 中最长的通用前缀

    我正在尝试解决 Leet Code 挑战14 最长公共前缀 https leetcode com problems longest common prefix 编写一个函数来查找字符串数组中最长的公共前缀字符串 如果没有公共前缀 则返回空字
  • 跨浏览器:禁用输入字段的不同行为(文本可以/不能复制)

    我有一个被禁用的输入 html 字段 在某些浏览器 Chrome Edge Internet Explorer 和 Opera 中可以选择并复制文本 但至少在 Firefox 中这是不可能的 您可以通过在不同浏览器中执行以下代码来测试
  • 如何修复nodejs Express服务器中的“MulterError:意外字段”?

    我正在设置一个服务器来从客户端上传 zip 文件 服务器运行express和multer来执行此操作 上传文件时 服务器抛出 MulterError 意外字段 错误 我无法弄清楚是什么导致了它 我尝试过使用 png 图像 效果很好 但对于

随机推荐

  • STM32 电机教程 12 - BLDC 闭环电流控制

    前言 无刷直流 Brushless Direct Current BLDC 电机是一种正快速普及的电机类型 它可在家用电器 汽车 航空航天 消费品 医疗 工业自动化设备和仪器等行业中使用 正如名称指出的那样 BLDC 电机不用电刷来换向 而
  • FastAPI 01--快速入门FastApi

    FastAPI 01 快速入门 介绍 第一个FastApi 案例 说明 介绍 FastAPI 是一个用于构建 API 的现代 快速 高性能 的 web 框架 使用 Python 3 6 并基于标准的 Python 类型提示 快速 可与 No
  • 推荐一个冷门又逆天的副业:Python兼职可月入10k+,成年人的世界,钱才是底气

    关于穷 去年有了一个更学术的说法 隐形贫困人口 就是因为有太多 种草达人 让我们为了物质生活超前消费 再加上不理财的话 那简直是雪上加霜 看到知乎上面最近有一个很火的问题 90后的你 现在拥有多少存款 你会看到人生百态 有人父母双亡 白手起
  • 正交矩阵的列向量组是标准正交向量组

  • Android+SpringBoot前后端分离实现登录注册

    Android SpringBoot前后端分离实现登录注册 一 登录 1 界面设计 2 Android端 1 布局文件 activity login 2 java文件 LoginActivity 下面给出找到IPv4地址的步骤 1 Win
  • postman和jmeter哪个是更好的接口测试工具?

    1 关于在哪个阶段使用哪个接口测试工具 对于单个的接口测试请求 我更喜欢用postman 可能比jmeter更好定位问题 在接口调试好后 我则更喜欢用jmeter进行一些简单的自动化请求 可能代替很多手工造数据和流程性的操作 列如 在开发交
  • 从入门到放弃系列--如何成为全栈工程师02

    未来3个月 你需要的html全部在这里 用html语言 可以写出目前你见到的所有类型的电脑界面 包括app 小程序 网页 注意 我说的是界面 说这个的意思是告诉你html是基础也很实用 是帮你打开全栈工程师的大门的第一步 所以 你需要熟练掌
  • 【torch】如何把把几个 tensor 连接起来?(含源代码)

    一 cat 在 PyTorch 中 要向一个 tensor 中添加元素 你通常需要创建一个新的 tensor 然后将元素添加到新的 tensor 中 PyTorch tensors 是不可变的 所以不能像列表一样直接追加元素 以下是如何实现
  • keytool 错误: java.io.IOException: Keystore was tampered with, or password was incorrect

    这里需要输入的密码不是证书的密码执行keytool import keystore file 这个命令提示需要输入密码 输入 changeit 信任证书 OK
  • opencv 通过网络连接工业相机_相机标定与测距

    0 概述 硬件 Realsense D435i 含imu AprilTag或棋盘格标定板 本文均使用棋盘格 说明 本文非手把手教你如何教程 需要一定的ROS基础和D435i相机调试基础 当然玩过其他相机也可以 写作过程参考了部分作者成果 如
  • 函数式编程—柯里化

    纯函数的作用和优势 1 可以安心的编写和使用 2 写的时候保证了函数的纯度 只是单纯实现自己的业务逻辑即可 不需要关心传入的内容是如何获得的或者依赖其他的外部变量是否已经发生了修改 3 在用的时候 确定输入的内容不会被任意篡改 并且自己确定
  • 玩具蛇

    include
  • 数据库SQLite

    数据库SQLite 了解最轻巧的数据库SQLite SQLite 是一款轻型的数据库 占用资源非常低 它的源代码不受版权限制 能够支持Windows Linux Unix等等主流的操作系统 同时能够跟很多程序语言相结合 比如 Tcl C P
  • 29 KVM管理系统资源-调整虚拟CPU绑定关系

    文章目录 29 KVM管理系统资源 调整虚拟CPU绑定关系 29 1 概述 29 2 操作步骤 29 KVM管理系统资源 调整虚拟CPU绑定关系 29 1 概述 把虚拟机的vCPU绑定在物理CPU上 即vCPU只在绑定的物理CPU上调度 在
  • 2-问过 chatgpt 的问题(天马行空想问什么问什么)

    目录 一 信号序列中大部分为 0 时 FFT 运算复杂度的计算 1 当fft运算时 大部分信号点为0的情况下 对fft的运算时间会有影响吗 2 大部分信号点为0的情况下 fft的运算复杂度计算 3 这里的时间复杂度 O N log
  • HTML5语音合成功能

    这篇文章主要介绍了HTML5语音合成功能的实现代码 本文通过实例代码给大家介绍的非常详细 具有一定的参考借鉴价值 需要的朋友参考下吧 可将该代码复制到chrome控制台中体验 let msg new SpeechSynthesisUtter
  • this和super

    this this总是置于当前对象的成员类作区分 this是当前对象的引用 就是说当前用构造函数建的对象是谁 这个this就代表谁 它是一个引用 this 总是指向自己本事 super 子类在重写了父类的方法后 常常还需要使用到父类中被重写
  • do while循环语句的学习以及练习

    今天学的是do while循环语句 先执行循环体 直到条件的表达式为false 与while循环语句的区别 while语句先判断条件 满足时执行循环体 do while语句先执行循环体 满足条件在执行 语法 do 循环体 while 条件
  • kinova-jaco2使用Moveit!控制真实机械臂抓取固定点物体

    kinova jaco2使用Moveit 控制真实机械臂抓取固定点物体 一 机械臂坐标系 坐标系方向 位姿方向 轴的起始点 二 启动机械臂和Moveit 三 实现抓取 python代码 python文件建议直接用python启动 四 遇到的
  • react hook之useMemo

    useMemo的作用 Pass a create function and an array of dependencies useMemo will only recompute the memoized value when one o