当前位置: 首页 > news >正文

[React]useMemoizedFn和useCallback对比

useMemoizedFn文档地址:https://ahooks.js.org/zh-CN/hooks/use-memoized-fn

hooks组件内什么时候会更新自定义函数

在 React 中,自定义的 Hooks 内部的函数在以下常见的几种情况下会被重新赋值,导致更新引用:

  • 组件重新渲染:

    当组件重新渲染时,Hooks 内部的函数会被重新执行,从而导致函数的重新赋值和更新引用。
    这意味着每次组件重新渲染时,Hooks 内部的函数会被重新计算,返回新的函数引用。

  • 依赖项发生变化(对于使用了依赖项的 Hooks):

    对于某些 Hooks,例如 useEffect、useMemo 和 useCallback,当其依赖项发生变化时,Hooks 内部的函数也会被重新执行。
    这会导致函数的重新赋值和更新引用,以确保在依赖项发生变化时能够获得最新的函数引用。

  • 闭包内部依赖的状态发生变化:

    自定义的 Hooks 可能使用闭包内部的状态或其他上下文中的值作为依赖,如果这些依赖的状态发生变化,Hooks 内部的函数也会重新执行。这样做是为了保持函数内部依赖的状态是最新的,避免出现闭包中使用过期数据的问题。

useCallback和useMemoizedFn对比

在 React 中,useMemoizedFnuseCallback 都用于优化函数组件的性能,但它们有一些不同点。

  1. 目的:
    • useMemoizedFn: 这不是 React 的内置 Hook,而是可能由第三方库提供的功能。它的目的是将一个函数 “记忆化”,类似于 useCallback,但在某些情况下,它提供了更多的功能,例如支持自定义的缓存策略和多个实例之间共享缓存等。
    • useCallback: 这是 React 的内置 Hook,专门用于记忆化函数。它的主要目的是在依赖项列表不变时,返回相同的函数引用,以减少函数的重新创建。
  2. 用法:
    • useMemoizedFn 的基本思想是将一个函数包装起来,并根据特定的条件缓存函数的结果,以便在相同的输入参数下再次调用时,直接返回缓存的结果,而不必重新计算。
    • useCallback 接受一个函数和依赖项列表,并在依赖项列表不变时返回记忆化的函数引用。
import React, { useState, useCallback } from 'react';
import { message } from 'antd';
import { useMemoizedFn } from 'ahooks';export default () => {const [count, setCount] = useState(0);const callbackFn = useCallback(() => {message.info(`Current count is ${count}`);}, [count]);const memoizedFn = useMemoizedFn(() => {message.info(`Current count is ${count}`);});return (<><p>count: {count}</p><buttontype="button"onClick={() => {setCount((c) => c + 1);}}>Add Count</button><div style={{ marginTop: 16 }}><button type="button" onClick={callbackFn}>call callbackFn</button><button type="button" onClick={memoizedFn} style={{ marginLeft: 8 }}>call memoizedFn</button></div></>);
};

在上面的示例中,callbackFn和memoizedFn效果是一样的,区别在于memoizedFn不用自己指定依赖。

useCallback:使用useMemo实现

使用 useMemo 来实现类似于 useCallback 的功能。事实上,useCallback 本质上就是使用 useMemo 来进行函数记忆化的一种简化形式。

useCallbackuseMemo 都是 React 的内置 Hook,它们都用于在依赖项不变时,返回记忆化的值。唯一的区别是 useCallback 是专门用于记忆化函数,而 useMemo 可以用于任何类型的值。

在 React 组件中,当函数作为依赖项传递给子组件时,通常会使用 useCallback 来确保子组件不会因为父组件的重新渲染而频繁地创建新的函数。然而,你也可以使用 useMemo 来达到相同的目的。

下面是一个使用 useMemo 实现类似于 useCallback 的示例:

import React, { useState, useMemo } from 'react';function useMyCallback(callback, deps) {return useMemo(() => callback, deps);
}const MyComponent = () => {const [count, setCount] = useState(0);// 使用 useMyCallback 记忆化函数const handleClick = useMyCallback(() => {console.log('Callback function called');setCount(count + 1);}, [count]);return (<div><p>Count: {count}</p><button onClick={handleClick}>Increment</button></div>);
};

在上面的示例中,我们定义了一个 useMyCallback Hook,它接受一个函数 callback 和一个依赖项列表 deps。在内部,我们使用 useMemo 将传入的 callback 进行记忆化,并返回记忆化的函数引用。

这样,当 count 不变时,handleClick 将返回相同的函数引用,避免了因为父组件重新渲染而导致的函数的频繁创建,从而实现了和 useCallback 类似的功能。

useMemoizedFn:使用useMemo实现

useMemoizedFn 是持久化 function 的 Hook,理论上,可以使用 useMemoizedFn 完全代替 useCallback。使用 useMemoizedFn,可以省略第二个参数 deps,同时保证函数地址永远不会变化

function useMemoizedFn(func){if(typeof func !== 'function') return// 通过 useRef 保持其引用地址不变,并且值能够保持值最新const funcRef = useRef(func)funcRef.current = useMemo(()=>{return func}, [func])const memoizedFn = useRef();if (!memoizedFn.current) {// 返回的持久化函数,调用该函数的时候,调用原始的函数memoizedFn.current = function(...args){return funcRef.current.apply(this, args)}}return memoizedFn.current
}

ahooks源码实现如下:

import { useMemo, useRef } from 'react';
import { isFunction } from '../utils';
import isDev from '../utils/isDev';
function useMemoizedFn(fn) {if (isDev) {if (!isFunction(fn)) {console.error("useMemoizedFn expected parameter is a function, got ".concat(typeof fn));}}var fnRef = useRef(fn);// why not write `fnRef.current = fn`?// https://github.com/alibaba/hooks/issues/728fnRef.current = useMemo(function () {return fn;}, [fn]);var memoizedFn = useRef();if (!memoizedFn.current) {memoizedFn.current = function () {var args = [];for (var _i = 0; _i < arguments.length; _i++) {args[_i] = arguments[_i];}return fnRef.current.apply(this, args);};}return memoizedFn.current;
}
export default useMemoizedFn;

useMemoizedFn 的实现中使用了两个 useRef 钩子,具有不同的用途:

  1. fnRef:这个 useRef 用于存储原始函数(fn)的引用。它被初始化为传递给 useMemoizedFn 函数的 fn 参数,并且其目的是始终保持函数的最新版本。使用它的原因是确保记忆化的函数(memoizedFn)能够始终调用最新版本的原始函数 fn,即使在渲染之间 fn 发生了变化。

  2. memoizedFn:这个 useRef 用于存储将由 useMemoizedFn 钩子返回的记忆化函数。其目的是在渲染间保持记忆化函数的引用。当 memoizedFn 首次创建时,它被赋值为一个新的函数,该函数调用最新版本的 fnRef.current 并传递提供的参数。然后,这个记忆化函数被返回并在组件中使用。

这样的实现通过使用两个独立的 useRef 钩子,确保记忆化函数能够正确地引用最新版本的原始函数,同时保持记忆化函数本身的稳定引用。

使用 useMemo 更新 fnRef.current 而不是直接赋值 fnRef.current = fn,是为了确保当 fn 依赖项发生变化时,记忆化函数会被重新计算。通过在 useMemo 中使用 [fn] 作为依赖项数组,记忆化函数将始终引用 fn 的最新版本,并在 fn 发生变化时相应地更新。

这个实现旨在确保 useMemoizedFn 返回的记忆化函数保持高效,并正确地反映原始函数的最新版本和其依赖项的最新变化。

参考文章

https://juejin.cn/post/7106061970184339464#heading-6

https://ahooks.js.org/zh-CN/hooks/use-memoized-fn

相关文章:

[React]useMemoizedFn和useCallback对比

useMemoizedFn文档地址&#xff1a;https://ahooks.js.org/zh-CN/hooks/use-memoized-fn hooks组件内什么时候会更新自定义函数 在 React 中&#xff0c;自定义的 Hooks 内部的函数在以下常见的几种情况下会被重新赋值&#xff0c;导致更新引用&#xff1a; 组件重新渲染&…...

计算机毕设 深度学习人体跌倒检测 -yolo 机器视觉 opencv python

文章目录 0 前言1.前言2.实现效果3.相关技术原理3.1卷积神经网络3.1YOLOV5简介3.2 YOLOv5s 模型算法流程和原理4.数据集处理3.1 数据标注简介3.2 数据保存 5.模型训练 6 最后 0 前言 &#x1f525; 这两年开始毕业设计和毕业答辩的要求和难度不断提升&#xff0c;传统的毕设题…...

完全背包

动态规划解题步骤 : 动态规划问题一般从三个步骤进行考虑。 步骤一:集合和集合的状态 所谓的集合&#xff0c;就是一些方案的集合。 用 g[i][j] 表示从前 i 种物品中进行选择&#xff0c;且总体积不大于 j 的各个选法获得的价值的集合。注意&#xff1a;g[i][j] 不是一个数…...

【软件测试】webdriver常用API演示(Java+IDEA+chrome浏览器)

1.元素定位方法 对象的定位应该是自动化测试的核心&#xff0c;要想操作一个对象&#xff0c;首先应该识别这个对象。一个对象就是一个人一样&#xff0c;他会有各种的特征&#xff08;属性&#xff09;&#xff0c;如比我们可以通过一个人的身份证号&#xff0c;姓名&#xf…...

Linux安装MySQL 8.1.0

MySQL是一个流行的开源关系型数据库管理系统&#xff0c;本教程将向您展示如何在Linux系统上安装MySQL 8.1.0版本。请按照以下步骤进行操作&#xff1a; 1. 下载MySQL安装包 首先&#xff0c;从MySQL官方网站或镜像站点下载MySQL 8.1.0的压缩包mysql-8.1.0-linux-glibc2.28-x…...

多线程面试相关的一些问题

面试题 1. 常见的锁策略相关的面试题 2. CAS相关的面试题 3. Synchronized 原理相关的面试题 4. Callable 接口相关的面试题 1. 常见的锁策略 乐观锁 vs 悲观锁 悲观锁: 总是假设最坏的情况&#xff0c;每次去拿数据的时候都认为别人会修改&#xff0c;所以每次在拿数据的时候都…...

【使用维纳滤波进行信号分离】基于维纳-霍普夫方程的信号分离或去噪维纳滤波器估计(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…...

Vue+axios如何解决跨域

1、为什么会产生跨域&#xff1f; 出于浏览器的同源策略限制。 同源策略&#xff08;Sameoriginpolicy&#xff09;是一种约定&#xff0c;是浏览器的一种安全机…...

网络安全系统中的守护者:如何借助威胁情报 (TI) 提高安全性

在这篇哈巴尔网站上的推文中&#xff0c;我们将解释 TI 缩写背后的含义、为什么需要它、Positive Technologies 收集哪些网络威胁数据以及如何帮助企业预防网络威胁。我们将以四种情况为例&#xff0c;说明公司如何使用 PT Threat Intelligence Feeds 来发现恶意活动并预防攻击…...

并发编程 - CompletableFuture

文章目录 Pre概述FutureFuture的缺陷类继承关系功能概述API提交任务的相关API结果转换的相关APIthenApplyhandlethenRunthenAcceptthenAcceptBoththenCombinethenCompose 回调方法的相关API异常处理的相关API获取结果的相关API DEMO实战注意事项 Pre 每日一博 - Java 异步编程…...

IPIDEA参展ChinaJoy!探索未来创新科技的峰会之旅

中国最大的国际数码互动娱乐展会ChinaJoy即将于7月28日在上海举行&#xff0c;届时将聚集全球来自22个国家和地区的领先科技公司、创业者和技术专家&#xff0c;为参观者呈现一系列引人入胜的展览和活动。而IPIDEA作为参展商之一&#xff0c;将为参观者带来一场关于数字科技的奇…...

2023最新ChatGPT商业运营版网站源码+支持ChatGPT4.0+GPT联网+支持ai绘画(Midjourney)+支持Mind思维导图生成

本系统使用Nestjs和Vue3框架技术&#xff0c;持续集成AI能力到本系统&#xff01; 支持GPT3模型、GPT4模型Midjourney专业绘画&#xff08;全自定义调参&#xff09;、Midjourney以图生图、Dall-E2绘画Mind思维导图生成应用工作台&#xff08;Prompt&#xff09;AI绘画广场自定…...

轮趣科技教育版ros小车键盘控制运动

我之前买的ros小车是单独买的底板&#xff0c;以为随便一个树莓派就可以&#xff0c;因为我以前有一个树莓派3B&#xff0c;后来买了单独的小车之后&#xff0c;发现只能使用树莓派4B&#xff0c;然后又单独买了一个树莓派4B&#xff0c;给装上镜像&#xff0c;安装ros-melodic…...

深入理解Python中的os.chdir()方法

深入理解Python中的os.chdir()方法 1. 简介 在Python中&#xff0c;os.chdir()方法用于改变当前的工作目录。工作目录是指当前正在执行的脚本所在的目录。通过使用os.chdir()方法&#xff0c;我们可以在脚本执行过程中切换到不同的目录。 在编写Python脚本时&#xff0c;我们…...

【Golang 接口自动化02】使用标准库net/http发送Post请求

目录 写在前面 发送Post请求 示例代码 源码分析 Post请求参数解析 响应数据解析 验证 发送Json/XMl Json请求示例代码 xml请求示例代码 总结 资料获取方法 写在前面 上一篇我们介绍了使用 net/http 发送get请求&#xff0c;因为考虑到篇幅问题&#xff0c;把Post单…...

LaTex语法(常用数学符号的语法和注意事项)

说明:[]括号表示把语法括起来&#xff0c;并不表示LaTex语法。 1. 求和符号(Σ) 这个符号的基本语法为&#xff1a;[\sum_{}^{}]。 符号有两种模式&#xff1a;内联数学模式(inside math mode)和显示数学模式(displayed math mode)。 内联数学模式&#xff1a;排版时使用各…...

Yunfly 一款高效、性能优异的node.js企业级web框架

介绍 Yunfly 一款高性能 Node.js WEB 框架, 使用 Typescript 构建我们的应用。 使用 Koa2 做为 HTTP 底层框架, 使用 routing-controllers 、 typedi 来高效构建我们的 Node 应用。 Yunfly 在 Koa 框架之上提升了一个抽象级别, 但仍然支持 Koa 中间件。在此基础之上, 提供了一…...

mac m1安装Centos9

先看结果&#xff08;在mac M1 安装centos8 安装不成功的原因大部分是没有找到正确的系统&#xff09; 由于Cnetos8 停服&#xff0c;现有mac m1 上能够按照的Centos8 并非由官方发布&#xff0c;因此寻找官方发布的能够在mac m1上安装的centos版本。 在YouTuBe上找到一个视频…...

深入理解mAP

0 介绍 mAP是目标检测任务最重要的评价指标。 mAP 是mean average precosion的缩写&#xff0c;mean 和 average都是平均的意思&#xff0c; 所以这个指标的计算涉及到2次平均。 mean是对所有类别的平均&#xff0c; 比如VOC 数据有20个类&#xff0c; 每个类别分别计算AP&…...

PostGis -基础、Springboot 整合、电子围栏处理

目的&#xff1a; 为什么要用PostgreSQL? 因为有时候我们需要存储 空间数据&#xff0c;如&#xff1a;存储一个 多边形 到数据。PostGis中 geometry、geography &#xff1a;基本空间数据类型&#xff0c;用于表达点线面等空间要素&#xff0c;具体类型涵盖了OGC的简单对象模…...

uniapp 对接腾讯云IM群组成员管理(增删改查)

UniApp 实战&#xff1a;腾讯云IM群组成员管理&#xff08;增删改查&#xff09; 一、前言 在社交类App开发中&#xff0c;群组成员管理是核心功能之一。本文将基于UniApp框架&#xff0c;结合腾讯云IM SDK&#xff0c;详细讲解如何实现群组成员的增删改查全流程。 权限校验…...

进程地址空间(比特课总结)

一、进程地址空间 1. 环境变量 1 &#xff09;⽤户级环境变量与系统级环境变量 全局属性&#xff1a;环境变量具有全局属性&#xff0c;会被⼦进程继承。例如当bash启动⼦进程时&#xff0c;环 境变量会⾃动传递给⼦进程。 本地变量限制&#xff1a;本地变量只在当前进程(ba…...

Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动

一、前言说明 在2011版本的gb28181协议中&#xff0c;拉取视频流只要求udp方式&#xff0c;从2016开始要求新增支持tcp被动和tcp主动两种方式&#xff0c;udp理论上会丢包的&#xff0c;所以实际使用过程可能会出现画面花屏的情况&#xff0c;而tcp肯定不丢包&#xff0c;起码…...

微软PowerBI考试 PL300-选择 Power BI 模型框架【附练习数据】

微软PowerBI考试 PL300-选择 Power BI 模型框架 20 多年来&#xff0c;Microsoft 持续对企业商业智能 (BI) 进行大量投资。 Azure Analysis Services (AAS) 和 SQL Server Analysis Services (SSAS) 基于无数企业使用的成熟的 BI 数据建模技术。 同样的技术也是 Power BI 数据…...

服务器硬防的应用场景都有哪些?

服务器硬防是指一种通过硬件设备层面的安全措施来防御服务器系统受到网络攻击的方式&#xff0c;避免服务器受到各种恶意攻击和网络威胁&#xff0c;那么&#xff0c;服务器硬防通常都会应用在哪些场景当中呢&#xff1f; 硬防服务器中一般会配备入侵检测系统和预防系统&#x…...

工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配

AI3D视觉的工业赋能者 迁移科技成立于2017年&#xff0c;作为行业领先的3D工业相机及视觉系统供应商&#xff0c;累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成&#xff0c;通过稳定、易用、高回报的AI3D视觉系统&#xff0c;为汽车、新能源、金属制造等行…...

聊一聊接口测试的意义有哪些?

目录 一、隔离性 & 早期测试 二、保障系统集成质量 三、验证业务逻辑的核心层 四、提升测试效率与覆盖度 五、系统稳定性的守护者 六、驱动团队协作与契约管理 七、性能与扩展性的前置评估 八、持续交付的核心支撑 接口测试的意义可以从四个维度展开&#xff0c;首…...

Device Mapper 机制

Device Mapper 机制详解 Device Mapper&#xff08;简称 DM&#xff09;是 Linux 内核中的一套通用块设备映射框架&#xff0c;为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程&#xff0c;并配以详细的…...

【Java学习笔记】BigInteger 和 BigDecimal 类

BigInteger 和 BigDecimal 类 二者共有的常见方法 方法功能add加subtract减multiply乘divide除 注意点&#xff1a;传参类型必须是类对象 一、BigInteger 1. 作用&#xff1a;适合保存比较大的整型数 2. 使用说明 创建BigInteger对象 传入字符串 3. 代码示例 import j…...

九天毕昇深度学习平台 | 如何安装库?

pip install 库名 -i https://pypi.tuna.tsinghua.edu.cn/simple --user 举个例子&#xff1a; 报错 ModuleNotFoundError: No module named torch 那么我需要安装 torch pip install torch -i https://pypi.tuna.tsinghua.edu.cn/simple --user pip install 库名&#x…...