ahooks中的核心hook-useRequest(上)

news2024/9/24 15:03:59

前言

useRequest是一个异步数据管理的hooks,是ahooks Hooks库的核心hook,因为其通过插件式组织代码,大部分功能都通过插件的形式来实现,所以其核心代码行数较少,简单易懂,还可以支持我们自定义扩展功能。可以说,useRequest能处理React项目绝大多数的网络请求场景。

让咱自己写可能写不出来,那就先从模仿开始,通过阅读useRequest的代码,从中学习大佬们的代码逻辑和思维处理。

前置hook了解

在useRequest的源码实现中使用到了一些其他的hooks

  • useCreation:useMemo 或 useRef 的替代品。
  • useLatest:返回当前最新值的 Hook, 可以避免闭包问题。
  • useMemoizedFn:useCallback的替代品 。
  • useMount:只在组件初始化时执行的 Hook。
  • useUnmount:在组件卸载(unmount)时执行的 Hook。
  • useUpdate:强制组件重新渲染的hook。

实现最基础的useRequest Hook

const { data, error, loading, cancel} = useRequest(service); 

useRequest通过定义一个class类Fetch 来维护相关的数据(data,loading等)和方法(run, refresh等)然后在useRequestImplement中创建Fetch实例,并返回实例属性和方法。

我对代码进行了拆分,保留了useRequest中核心的功能,该hook接收一个promise,需要返回data、error、loading、cancel状态。

const useRequestImplement = (service: Promise<any>) => {const serviceRef = useLatest(service);const update = useUpdate();const fetchInstance = useCreation(() => {return new Fetch(serviceRef, update);}, []);useMount(() => {// useCachePlugin can set fetchInstance.state.params from cache when initconst params = fetchInstance.state.params ?? [];// @ts-ignorefetchInstance.runAsync(...params);});useUnmount(() => {fetchInstance.cancel();});return {loading: fetchInstance.state.loading,data: fetchInstance.state.data,error: fetchInstance.state.error,params: fetchInstance.state.params || [],cancel: useMemoizedFn(fetchInstance.cancel.bind(fetchInstance)),};
}; 
export default class Fetch<TData, TParams extends any[]> {public count: number = 0;public state = {loading: false,params: undefined,data: undefined,error: undefined,};constructor(public serviceRef, public subscribe) {}setState(s) {this.state = {...this.state,...s,};this.subscribe();}async runAsync(...params: TParams): Promise<any> {this.count += 1;const currentCount = this.count;this.setState({loading: true,params,});try {const servicePromise = this.serviceRef.current(...params);const res = await servicePromise;if (currentCount !== this.count) {return new Promise(() => {});}this.setState({data: res,error: undefined,loading: false,});return res;} catch (error) {if (currentCount !== this.count) {// prevent run.then when request is canceledreturn new Promise(() => {});}this.setState({error,loading: false,});throw error;}}cancel() {this.count += 1;this.setState({loading: false,});}refreshAsync() {// @ts-ignorereturn this.runAsync(...(this.state.params || []));}
} 

实现剩余核心功能

接收用户的自定义配置,包括manual(手动模式)和一些回调函数(onBefore,onSuccess, onError,onFinally)

const useRequestImplement = (service: Promise<any>, options) => {const { manual = false, ...rest } = options;const fetchOptions = {manual,...rest,};// const serviceRef = useLatest(service);// const update = useUpdate();// const fetchInstance = useCreation(() => {return new Fetch(serviceRef, fetchOptions, update);// }, []);fetchInstance.options = fetchOptions;useMount(() => {if (!manual) {const params = fetchInstance.state.params || options.defaultParams || [];// @ts-ignorefetchInstance.run(...params);}});// useUnmount(() => {// fetchInstance.cancel();// });return {// loading: fetchInstance.state.loading,// data: fetchInstance.state.data,// error: fetchInstance.state.error,// params: fetchInstance.state.params || [],// cancel: useMemoizedFn(fetchInstance.cancel.bind(fetchInstance)),refresh: useMemoizedFn(fetchInstance.refresh.bind(fetchInstance)),refreshAsync: useMemoizedFn(fetchInstance.refreshAsync.bind(fetchInstance)),run: useMemoizedFn(fetchInstance.run.bind(fetchInstance)),runAsync: useMemoizedFn(fetchInstance.runAsync.bind(fetchInstance)),mutate: useMemoizedFn(fetchInstance.mutate.bind(fetchInstance)),};
}; 
export default class Fetch<TData, TParams extends any[]> {
// public count: number = 0;
// 
// public state = {
// loading: false,
// params: undefined,
// data: undefined,
// error: undefined,
// };constructor(public serviceRef, public options, public subscribe) {this.state = {...this.state,loading: !options.manual,};}// setState(s) {// this.state = {// ...this.state,// ...s,// };// this.subscribe();// }
// async runAsync(...params: TParams): Promise<any> {
// this.count += 1;
// const currentCount = this.count;
// 
// this.setState({
// loading: true,
// params,
// });
// this.options.onBefore?.(params);
// try {
// const servicePromise = this.serviceRef.current(...params);
// const res = await servicePromise;
// if (currentCount !== this.count) {
// return new Promise(() => {});
// }
// this.setState({
// data: res,
// error: undefined,
// loading: false,
// });
// this.options.onSuccess?.(res, params);this.options.onFinally?.(params, res, undefined);
// 
// return res;
// } catch (error) {
// if (currentCount !== this.count) {
// // prevent run.then when request is canceled
// return new Promise(() => {});
// }
// this.setState({
// error,
// loading: false,
// });
// this.options.onError?.(error, params);this.options.onFinally?.(params, undefined, error);
// throw error;
// }
// }

// cancel() {
// this.count += 1;
// this.setState({
// loading: false,
// });
// }
// 
// refreshAsync() {
// // @ts-ignore
// return this.runAsync(...(this.state.params || []));
// }run(...params: TParams) {this.runAsync(...params).catch((error) => {if (!this.options.onError) {console.error(error);}});}refresh() {// @ts-ignorethis.run(...(this.state.params || []));}mutate(data?: TData | ((oldData?: TData) => TData | undefined)) {const targetData = isFunction(data) ? data(this.state.data) : data;this.setState({data: targetData,});}
} 

runAsync和run

runAsync方法返回一个promise,使用runAsync时,当请求报错会中断后续操作,需要手动捕获异常。

run方法则对runAsync进行了封装,帮助我们了捕获异常,或可以通过options.onError来处理异常行为。

refresh和refreshAsync

useRequest维护了一份params,调用run()和runAsync()的时候会同时更新params。以便给refresh和refreshAsync方法使用

cancel

useRequest维护了一个count。

而runAsync方法本身也维护一个currentCount。

每次调用runAsync时,count进行一次++操作,然后将其赋值给currentCount。

每次cancel方法count会再进行一次++操作。通过比较count和currentCount的值来判断用户是否进行了取消操作,进行相应的处理

mutate

支持立即修改 useRequest 返回的 data 参数。

mutate 的用法与 React.setState 一致,支持 mutate(newData) 和 mutate((oldData) => newData) 两种写法。

小结

以上是useRequest hook 的基本功能,剩余功能如loading状态延时、请求防抖、节流、数据缓存等功能都是通过插件的形式进行实现的,具体实现可以看 ahooks中的核心hook-useRequest(下)

最后

整理了一套《前端大厂面试宝典》,包含了HTML、CSS、JavaScript、HTTP、TCP协议、浏览器、VUE、React、数据结构和算法,一共201道面试题,并对每个问题作出了回答和解析。

有需要的小伙伴,可以点击文末卡片领取这份文档,无偿分享

部分文档展示:



文章篇幅有限,后面的内容就不一一展示了

有需要的小伙伴,可以点下方卡片免费领取

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

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

相关文章

基础知识总结

Java 基础 1. JDK 和 JRE 有什么区别&#xff1f; JDK&#xff1a; Java Development Kit 的简称&#xff0c;Java 开发工具包&#xff0c;提供了 Java 的开发环境和运行环境。JRE&#xff1a; Java Runtime Environment 的简称&#xff0c;Java 运行环境&#xff0c;为 Java…

Android App加固原理与技术历程

App为什么会被破-jie入侵 随着黑客技术的普及化平民化&#xff0c;App&#xff0c;这个承载我们移动数字工作和生活的重要工具&#xff0c;不仅是黑客眼中的肥肉&#xff0c;也获得更多网友的关注。百度一下“App破-jie”就有5290万条结果。 ​ 一旦App被破-jie&#xff0c;不…

【图像处理】图像的锐化操作 | 边缘检测sobel算子,拉普拉斯算子,Canny算子| opencv

文章目录前言一、一阶导数算子&#xff1a;sobel算子二、二阶导数算子&#xff1a;拉普拉斯算子三.Canny算子前言 参考视频&#xff1a;opencv教程&#xff08;跟着视频敲了一遍代码&#xff09; 参考教材&#xff1a;《数字图像处理基础》 作者&#xff1a;朱虹 一、一阶导数…

【unity笔记】图解 Vector3.SignedAngle()方法的返回值

这个方法可以理解为&#xff1a;“两个向量之间的夹角&#xff08;有符号的&#xff09;”。 我会将它想象成&#xff1a;将两个向量都放在坐标原点&#xff0c;一个向量要向哪个方向旋转多少度 才能与另一个向量重合。 于是我在坐标原点放置了两个向量&#xff1a;OB和OA。 …

Java Object 类

Java Object 类是所有类的父类&#xff0c;也就是说 Java 的所有类都继承了 Object&#xff0c;子类可以使用 Object 的所有方法。 Object 类位于 java.lang 包中&#xff0c;编译时会自动导入&#xff0c;我们创建一个类时&#xff0c;如果没有明确继承一个父类&#xff0c;那…

Python采集股票数据信息

前言 今天打算来整整股票&#xff0c;简简单单的采集一些股票数据 对这个有兴趣的就一起来瞧瞧吧 准备 开发环境 & 第三方模块 解释器版本: python 3.8代码编辑器: pycharm 2021.2requests: pip install requests 爬虫pyecharts: pip install pyecharts 数据分析pandas…

图像属性操作

数字图像处理本质是对多维矩阵的操作。按照处理对象不同&#xff0c;可分为黑白图像处理&#xff0c;灰度图像处理&#xff0c;彩色图像处理。按照处理方法分为空间域处理和频域处理。按照策略分为全局处理和局部处理。 #一般步骤输入图像 多维数组 数组运算 图像…

排序算法:堆排序,快速排序,归并排序。内附完整代码和算法思路详解。

目录 一&#xff1a;堆排序 1.1&#xff1a;堆的数据结构 1.2&#xff1a;大小根堆 1.3&#xff1a;向下调整算法构建小根堆 1.4&#xff1a;堆排序思想 二&#xff1a;快速排序 2.1&#xff1a;快排单趟思想 三&#xff1a;归并排序 3.1&#xff1a;分组过程 3.2&am…

JSP ssh学习管理系统myeclipse开发mysql数据库MVC模式java编程计算机网页设计

一、源码特点 JSP ssh学习管理系统是一套完善的web设计系统&#xff08;系统采用ssh框架进行设计开发&#xff09;&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式 开发。开发环境为TOMCAT7.0,Mye…

MMDetection框架入门教程之环境部署

创建虚拟环境 打开Anaconda Prompt&#xff0c;创建一个新的虚拟环境mmlab。注意我这里选择了python3.6&#xff0c;因为在python3.7下出现了mmdet3d.apis中的类无法无法import的情况&#xff08;但mmdet3d可以import&#xff09;&#xff0c;暂时不清楚原因。 conda create …

SQLSERVER,求平均数,最大,最小,中位数,众数

SQLSERVER&#xff0c;求平均数&#xff0c;最大&#xff0c;最小&#xff0c;中位数&#xff0c;众数 SQLSERVER&#xff0c;求平均数&#xff0c;最大&#xff0c;最小&#xff0c;中位数&#xff0c;众数 SELECT -- *, t1.remark, t1.my_count, t1.my_sum, …

ArcGIS Python ​影像批量裁剪

该工具在&#xff1a;“14综合\工具箱.tbx\影像裁剪\按记录批量裁剪影像”&#xff0c;影像数据按矢量面要素批量裁剪&#xff0c;界面如图14-5所示。 图14-5 影像批量裁剪 按一个矢量面数据&#xff0c;按字段值相同的融合在一起裁剪影像&#xff0c;字段值是裁剪后的影像名字…

【再学Tensorflow2】TensorFlow2的高层封装

TensorFlow2的高层封装使用Tensorflow2构建模型的3种方法使用Sequential按层顺序构建模型使用函数式API创建任意结构的模型使用Model子类化创建自定义模型训练模型的3种方法内置fit方法内置train_on_batch方法自定义训练循环使用GPU训练模型使用单GPU训练模型使用多GPU训练模型…

ti3090安装cuda113+cudnn+anaconda+yolopose过程

wget https://developer.download.nvidia.com/compute/cuda/11.3.1/local_installers/cuda_11.3.1_465.19.01_linux.run sudo sh cuda_11.3.1_465.19.01_linux.run 如果有了nvidia driver&#xff0c;可以不用install driver选项。 配置环境变量&#xff1a; export PATH“/…

十一、kubernetes核心技术Label详解、实例

1、概述 Label是kubernetes系统中的一个重要概念。它的作用就是在资源上添加标识&#xff0c;用来对它们进行区分和选择。 Label的特点&#xff1a; 一个Label会以key/value键值对的形式附加到各种对象上&#xff0c;如Node、Pod、Service等等 一个资源对象可以定义任意数量的…

【技术博客】文本挖掘之LDA主题模型

文本挖掘之LDA主题模型 作者&#xff1a;郑培 引言 主题模型是文本挖掘的重要工具&#xff0c;近年来在工业界和学术界都获得了非常多的关注。在文本挖掘领域&#xff0c;大量的数据都是非结构化的&#xff0c;很难从信息中直接获取相关和期望的信息&#xff0c;一种文本挖掘…

ArcGIS基础实验操作100例--实验23提取栅格有效边界值

本实验专栏来自于汤国安教授《地理信息系统基础实验操作100例》一书 实验平台&#xff1a;ArcGIS 10.6 实验数据&#xff1a;请访问实验1&#xff08;传送门&#xff09; 基础编辑篇--实验23 提取栅格有效边界值 目录 一、实验背景 二、实验数据 三、实验步骤 &#xff08;…

sqlserver 求平均数,中位数,众数

sqlserver 求平均数&#xff0c;中位数&#xff0c;众数 sqlserver 聚合函数结合 窗口函数的巧妙使用。 SELECT -- *, t1.remark, t1.my_count, t1.my_sum, t1.my_avg, t1.my_min, t1.my_max, /*my_median : 中位数*/ t2.my_median, /*my_mos…

保姆教程系列二、Redis高可用(主从同步+哨兵模式)

系列文章目录 &#xff01;&#xff01;&#xff01;是的没错&#xff0c;胖友们&#xff0c;保姆教程系列又更新了&#xff01;&#xff01;&#xff01; 保姆教程系列一、Redis部署 so easy 保姆教程系列二、Redis高可用&#xff08;主从同步哨兵模式&#xff09; 保姆教程系…

2008-2020年上市公司环境治理费用

2008-2020年上市公司环境治理费用 1、时间区间为&#xff1a;2008-2020年 2、指标包括&#xff1a;统计截止日期、证券代码、证券简称、本期金额、上期金额、上市公司排污费、环保费、绿化费、环保支出等有关环境治理费用 3、指标说明&#xff1a; EndDate [统计截止日期] …