[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

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

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

相关文章

AR开发平台 | 探索AR技术在建筑设计中的创新应用与挑战

随着AR技术的不断发展和普及&#xff0c;越来越多的建筑师开始探索AR技术在建筑设计中的应用。AR(增强现实)技术可以通过将虚拟信息叠加到现实场景中&#xff0c;为设计师提供更加直观、真实的建筑可视化效果&#xff0c;同时也可以为用户带来更加沉浸式的体验。 AR开发平台广…

Docker Compose 安装与使用(常用指令)

一、简介 Docker Compose 是一个编排多容器分布式部署的工具&#xff0c;提供命令集管理容器化应用的完整开发周期&#xff0c;包括服务构建、启动和停止。使用步骤&#xff1a;1. 利用 Dockerfile 定义运行环境镜像 2. 使用 docker-compose.yml 家义组成应用的各服务 3. 运行 …

flink数据流 单(kafka)流根据id去重

方法1 不推荐 package com.yy.uniqimport org.apache.flink.configuration.{Configuration, RestOptions} import org.apache.flink.streaming.api.scala.StreamExecutionEnvironment import org.apache.flink.table.api.bridge.scala.StreamTableEnvironmentimport java.time…

Makefile学习2

文章目录 Makefile学习2Makefile条件判断ifeq 关键字ifneq 关键字ifdef 关键字ifndef 关键字 Makefile函数用户自定义函数文本处理函数 Makefile 通配符 Makefile学习2 Makefile条件判断 使用条件判断&#xff0c;可以让make在编译程序时&#xff0c;根据不同的情况&#xff…

IO进程线程day5(2023.8.2)

一、Xmind整理&#xff1a; 父进程会拷贝文件描述符表给子进程&#xff1a; 二、课上练习&#xff1a; 练习1&#xff1a;①从终端获取一个文件的路径以及名字。②若该文件是目录文件&#xff0c;则将该文件下的所有文件的属性显示到终端&#xff0c;类似ls -l该文件夹③若该文…

【Linux命令200例】touch用来创建新的文件或者修改已有文件

&#x1f3c6;作者简介&#xff0c;黑夜开发者&#xff0c;全栈领域新星创作者✌&#xff0c;阿里云社区专家博主&#xff0c;2023年6月csdn上海赛道top4。 &#x1f3c6;本文已收录于专栏&#xff1a;Linux命令大全。 &#x1f3c6;本专栏我们会通过具体的系统的命令讲解加上鲜…

代码随想录算法训练营day52

文章目录 Day52 最长递增子序列题目思路代码 最长连续递增序列题目思路代码 最长重复子数组题目思路代码 Day52 最长递增子序列 300. 最长递增子序列 - 力扣&#xff08;LeetCode&#xff09; 题目 给你一个整数数组 nums &#xff0c;找到其中最长严格递增子序列的长度。 …

前端视频播放技术概览

转眼间&#xff0c;2023 年已进入下半场&#xff0c;在这样一个时间节点下&#xff0c;长视频平台如爱奇艺、优酷、腾讯视频等&#xff0c;以及短视频平台如抖音、快手等&#xff0c;对大家来说早已是司空见惯的事物。然而&#xff0c;在我们追剧、刷弹幕的时候&#xff0c;很少…

阿里云国际版在使用过程中应该注意什么呢?

为确保系统稳定性&#xff0c;用户不得进行以下操作。否则&#xff0c;阿里云可能无法解决由以下违规操作引起的问题&#xff1a; 1) Windows系统中的PV Drivers 程序不可删除 PV Drivers程序为服务器虚拟化驱动程序&#xff0c;请不要针对该程序进行任何操作&#xff0c;如果删…

电压放大器工作在什么状态

电压放大器是一种广泛应用于电子电路中的基本电路元件&#xff0c;其主要功能是将输入信号的电压放大到所需的输出电压幅值&#xff0c;并且保持信号的形状不变。在实际电路设计中&#xff0c;电压放大器的工作状态会受到多种因素的影响&#xff0c;比如输入信号的频率、放大倍…

iOS--runtime

什么是Runtime runtime是由C和C、汇编实现的一套API&#xff0c;为OC语言加入了面向对象、运行时的功能运行时&#xff08;runtime&#xff09;将数据类型的确定由编译时推迟到了运行时平时编写的OC代码&#xff0c;在程序运行过程中&#xff0c;最终会转换成runtime的C语言代…

爱尔眼科四川省区“同心博爱 光明工程”“西部健康公益行”炉霍站启动

8月1日&#xff0c;“同心博爱 光明工程”“西部健康公益行”炉霍站出征仪式在四川爱尔眼科医院隆重举行。 此次公益活动由民革成都市委会、中共锦江区委统战部指导&#xff0c;如意树爱心促进会主办&#xff0c;民革锦江区总支部、爱尔眼科四川省区支持&#xff0c;四川爱尔眼…

手把手教你从零开始搭建个人博客

随着技术的进步和用户需求的变化&#xff0c;个人博客的形式和内容一直在不停地演变。为了给读者提供更丰富、有趣的阅读体验&#xff0c;搭建个人博客的网站一直在寻找更好的优化方法。所以现在出现了一批功能更完善的个人博客搭建软件&#xff0c;今天looklook就以HelpLook为…

C++设计模式之适配器设计模式

文章目录 C适配器设计模式什么是适配器设计模式该模式有什么优缺点优点缺点 如何使用 C适配器设计模式 什么是适配器设计模式 适配器设计模式是一种行为型设计模式&#xff0c;它允许你将两个不兼容的接口组合在一起&#xff0c;使它们能够协同工作。 该模式有什么优缺点 优…

Boost开发指南-3.10singleton_pool

singleton_pool singleton_pool与 pool的接口完全一致&#xff0c;可以分配简单数据类型&#xff08;POD&#xff09;的内存指针&#xff0c;但它是一个单件。 singleton_pool位于名字空间boost&#xff0c;为了使用singleton_pool组件&#xff0c;需要包含头文件<boost/p…

Activiity跳转startActivity源码分析Activity启动流程(下)

调用ActivityThread子类ClientTranslationHandler的scheduleTranslation 注意上图有个sendMessage的 接着会执行translacationExecutor的execute方法。 都会走cycleToPath方法 cycleToPath方法对应的performLifecycleSequence 调用Actvitiy各个生命周期。 然后是第二种情况&am…

设计模式行为型——命令模式

目录 什么是命令模式 命令模式的实现 命令模式角色 命令模式类图 命令模式举例 命令模式代码实现 命令模式的特点 优点 缺点 使用场景 注意事项 什么是命令模式 命令模式&#xff08;Command Pattern&#xff09;是一种数据驱动的设计模式&#xff0c;它属…

【C#学习笔记】值类型(1)

虽然拥有编程基础的人可以很快地上手C#&#xff0c;但是依然需要学习C#的特性和基础。本系列是本人学习C#的笔记&#xff0c;完全按照微软官方文档编写&#xff0c;但是不适合没有编程基础的人。 文章目录 .NET 体系结构Hello&#xff0c;World类型和变量&#xff08;重要&…

Hive的堵塞问题和表锁问题原因查找

Hive的堵塞问题可能是由多种原因引起的。下面是一些可能的原因和解决方法&#xff1a; 数据倾斜&#xff1a;如果某个字段的值分布不均匀&#xff0c;可能会导致某些任务处理的数据量过大&#xff0c;从而造成堵塞。可以通过使用分桶或者使用JOIN操作时进行数据倾斜处理来解决这…

docker容器创建私有仓库(第三篇)

目录 六、创建私有仓库 七、Docker资源限制 7.1、CPU使用率 7.2、CPU共享比例 7.3、CPU周期限制 7.4、CPU核心限制 7.5、CPU 配额控制参数的混合案例 7.6、内存限制 7.7、Block IO 的限制 7.8、限制bps 和iops 8、Docker数据持久化 8.1、数据持久化介绍 8.2、Volum…