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

【react.js + hooks】使用 useLoading 控制加载

在页面上 loading(加载)的效果十分常见,在某些场景下,一个页面上甚至可能有特别多的 loading 存在,此时为每一个 loading 专门创建一个 state 显然太过繁琐,不如试试写一个 useLoading 来集中管理!

构思分析

状态形式

当页面上有众多的 loading 时,我们需要能区分它们,因此 useLoading 存储的 state 应当是 KV 式 或者数组式,KV 更方便,我们将采用 KV 式,K 代表 loading 的标识名称,V代表是否 loading。

返回形式

useLoading 应当提供哪些 api 呢?返回 state 和 setState 是必要的,此外,提供根据 K 的 setLoading 以及 根据 K 的 onloading 和 unloading 也是 非常使用的。

初始化方式

既然已经确定了 state 为 KV 式,初始化给个对象即可。

代码实现

接口和类型声明

声明 UseLoading 的入参为初始 state ,并定义其为泛型,默认为 Record<string, Boolean>,并按照 useHooks 的习惯,返回一个 只读 的数组,第一项为 state,第二项为 set,第三项为 onloading,第四项为 unloading。
此外,我们需要为 set 额外定义一个接口,用于 (K,V)({k,v}) 两种入参形式的函数重载。

export interface SetLoading<T = Record<string, Boolean | number>,K extends keyof T  = keyof T
> {(key: K, value?: boolean): void;(key: K, setAction: (pre: boolean | number)=> boolean): void;(state: Record<K, boolean>): void;(setAction: (pre: T) => T): void;
}export interface UseLoading<T = Record<string, boolean>> {(loadingMap: T): readonly [T,SetLoading,(key: keyof T) => void,(key: keyof T) => void];
}
具体实现
export const useLoading: UseLoading = <T = Record<string, boolean | number>>(initialLoadingMap: T
) => {const [loading, _setLoading] = useState<T>(initialLoadingMap);const setLoading: SetLoading<T, keyof T> = (args1 , value = true) => {if (typeof args1 === "object") {_setLoading((pre) => ({ ...pre, ...args1 }));return;} else if (typeof args1 === "function") {_setLoading((pre) => args1(pre));return;} else {const key = args1;if (typeof value === "function") {_setLoading((pre) => ({ ...pre, [key]: value(pre[key]) }));} else {_setLoading((pre) => ({ ...pre, [key]: value }));}}};const onLoading = (key: keyof typeof loading) => {_setLoading((pre) => ({ ...pre, [key]: true }));};const unLoading = (key: keyof typeof loading) => {_setLoading((pre) => ({ ...pre, [key]: false }));};return [loading,setLoading,onLoading,unLoading,] as unknown as ReturnType<UseLoading>;
};

到这里,基础的 useLoading 就实现了,以下是简单的使用示例:

export default function Demo(){const [loading, setLoading] = useLoading({button1: false,button2: false,})const handleClick = (key: 'button1'|'button2') => {setLoading()}return(<><button onClick={() => setLoading('button1')}>{loading.button1? 'loading':'button1'}</button><button onClick={() => setLoading('button1')}>{loading.button2? 'loading':'button2'}</button></>)
}

进阶

有时候,我们的 loading 只是效果,并不阻止用户操作,那么当用户连续进行点击等操作时,我们希望 loading 效果应当延续下去,此时只使用上面的 useLoding 显然乏力,我们需要额外维护一个具有计数性质的 state(数字数组,Promise数组等) 配合使用,这将使代码变得异常臃肿。

换位思考一下,我们需要计数的特性,useLoding 使用的 state 是 Boolean,那么有没有办法可以同时兼备布尔值和数字的特性呢?在 JS/TS 中,boolean 本身就支持加减操作,加减后会隐式转换为数字,答案已经呼之欲出了!允许我们的 useLodng 使用数字作为每个 loading 的值,即可完美的升级 useLoading,并完全兼容基础版的 useLoading,我们只需要做一些小小的改变:

接口和类型声明

在这里,将 boolean 都替换为 boolean | number 即可。
其次,我们提供了2个额外的 api:plusminus,即递增和递减。
此外,我还为 useLoading 新加了一个 returnType 入参的重载,因为有些人可能更偏爱对象而不是数组,比如笔者自己。

export interface UseLoading<T = Record<string, boolean | number>> {(loadingMap: T): readonly [T,SetLoading,(key: keyof T) => void,(key: keyof T) => void,(key: keyof T) => void,(key: keyof T) => void];(loadingMap: T, returnType: "object"): Readonly<{values: T;set: SetLoading;on: (key: keyof T) => void;un: (key: keyof T) => void;plus: (key: keyof T) => void;minus: (key: keyof T) => void;}>;
}export interface SetLoading<T = Record<string, boolean | number>,K extends keyof T = keyof T
> {(key: K, value?: boolean | number): void;(key: K, setAction: (pre: boolean | number) => boolean | number): void;(state: Record<K, boolean | number>): void;(setAction: (pre: T) => T): void;
}
代码实现

在这里,实现上与 基础的 useLoading 几乎完全相同,只是多了几个新的返回和一套对象形式的返回。

// @ts-ignore
export const useLoading: UseLoading = <T = Record<string, boolean | number>>(loadingMap: T,returnType: "array" | "object" = "array"
) => {const [loading, _setLoading] = useState(loadingMap);const setLoading: SetLoading<T, keyof T> = (args1, value = true) => {if (typeof args1 === "object") {_setLoading((pre) => ({ ...pre, ...args1 }));return;} else if (typeof args1 === "function") {_setLoading((pre) => args1(pre));return;} else {const key = args1;if (typeof value === "function") {_setLoading((pre) => ({ ...pre, [key]: value(pre[key]) }));} else {_setLoading((pre) => ({ ...pre, [key]: value }));}}};const onLoading = (key: keyof typeof loading) => {_setLoading((pre) => ({ ...pre, [key]: 1 }));};const unLoading = (key: keyof typeof loading) => {_setLoading((pre) => ({ ...pre, [key]: 0 }));};const plusLoading = (key: keyof typeof loading) => {_setLoading((pre) => ({ ...pre, [key]: (pre[key] as number) + 1 }));};const minusLoading = (key: keyof typeof loading) => {_setLoading((pre) => ({ ...pre, [key]: (pre[key] as number) - 1 }));};if (returnType === "array") {return [loading,setLoading,onLoading,unLoading,plusLoading,minusLoading,] as const;} else {return {values: loading,set: setLoading,on: onLoading,un: unLoading,plus: plusLoading,minus: minusLoading,} as const;}
};

现在,一个进阶版的 useLoading 就完成了,你完全可以把它当作普通的 useLoading 穿 boolean 用,如果有计数的需要你就可以把它当数字用:

function sleep<T>(time: number) {return new Promise<void>(function (resolve) {setTimeout(() => {resolve();}, time);});
}export default function Demo(){const { values: loading, plus, minus } = useLoading({ button1: false }, 'object');const click = async () => {plus('button1')await sleep(1000);minus('button1')}
}return(<><button onClick={click}>{loading.button1? 'loading':'button1'}</button></>)
}

相关文章:

【react.js + hooks】使用 useLoading 控制加载

在页面上 loading&#xff08;加载&#xff09;的效果十分常见&#xff0c;在某些场景下&#xff0c;一个页面上甚至可能有特别多的 loading 存在&#xff0c;此时为每一个 loading 专门创建一个 state 显然太过繁琐&#xff0c;不如试试写一个 useLoading 来集中管理&#xff…...

Cordova系列之化繁为简:打造全场景适用的Cordova组件

前言 在我之前的文章 Cordova初探 的开篇中说到了Cordova在Android应用开发中的一个显著的局限性就是我们的Activity必须继承其提供的CordovaActivity。这种设计对于那些追求个性化UI设计的项目而言&#xff0c;显得尤为受限。 其实也可以理解&#xff0c;Cordova主要旨在为前…...

Flink之Catalog

Catalog Catalog概述Catalog分类 GenericInMemoryCatalogJdbcCatalog下载JAR包及使用重启操作创建Catalog查看与使用Catalog自动初始化catalog HiveCatalog下载JAR包及使用重启操作hive metastore服务创建Catalog查看与使用CatalogFlink与Hive中操作自动初始化catalog 用户自定…...

计算机网络——物理层-传输方式(串行传输、并行传输,同步传输、异步传输,单工、半双工和全双工通信)

目录 串行传输和并行传输 同步传输和异步传输 单工、半双工和全双工通信 串行传输和并行传输 串行传输是指数据是一个比特一个比特依次发送的。因此在发送端和接收端之间&#xff0c;只需要一条数据传输线路即可。 并行传输是指一次发送n个比特&#xff0c;而不是一个比特&…...

男科医院服务预约小程序的作用是什么

医院的需求度从来都很高&#xff0c;随着技术发展&#xff0c;不少科目随之衍生出新的医院的&#xff0c;比如男科医院、妇科医院等&#xff0c;这使得目标群体更加精准&#xff0c;同时也赋能用户可以快速享受到服务。 当然相应的男科医院在实际经营中也面临痛点&#xff1a;…...

有没有实时检测微信聊天图片的软件,只要微信收到了有二维码的图片就把它提取出来?

10-2 如果你有需要自动并且快速地把微信收到的二维码图片保存到指定文件夹的需求&#xff0c;那本文章非常适合你&#xff0c;本文章教你如何实现自动保存微信收到的二维码图片到你指定的文件夹中&#xff0c;助你快速扫码&#xff0c;比别人领先一步。 首先需要准备好的材料…...

core-site.xml,yarn-site.xml,hdfs-site.xml,mapred-site.xml配置

core-site.xml <?xml version"1.0" encoding"UTF-8"?> <?xml-stylesheet type"text/xsl" href"configuration.xsl"?> <!--Licensed under the Apache License, Version 2.0 (the "License");you may no…...

数据分析实战 | KNN算法——病例自动诊断分析

目录 一、数据及分析对象 二、目的及分析任务 三、方法及工具 四、数据读入 五、数据理解 六、数据准备 七、模型训练 八、模型评价 九、模型调参 十、模型改进 十一、模型预测 一、数据及分析对象 CSV文件——“bc_data.csv” 数据集链接&#xff1a;https://dow…...

JS实现数据结构与算法

队列 1、普通队列 利用数组push和shif 就可以简单实现 2、利用链表的方式实现队列 class MyQueue {constructor(){this.head nullthis.tail nullthis.length 0}add(value){let node {value}if(this.length 0){this.head nodethis.tail node}else{this.tail.next no…...

计算机毕业设计 基于SpringBoot的驾校管理系统的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…...

S7-1200PLC和SMART PLC开放式以太网通信(UDP双向通信)

S7-1200PLC的以太网通信UDP通信相关介绍还可以参考下面文章链接: 博途PLC开放式以太网通信TRCV_C指令应用编程(运动传感器UDP通信)-CSDN博客文章浏览阅读2.8k次。博途PLC开放式以太网通信TSENG_C指令应用,请参看下面的文章链接:博途PLC 1200/1500PLC开放式以太网通信TSEND_…...

作用域插槽slot-scope

一般用于组件封装&#xff0c;将使用props传入组件的数据再次调出来或者单纯调用组件中的数据。也可用于为组件某个部分自定义样式以及为某次使用组件自定义样式。 直接拿elementui的el-table举例&#xff1a; <template><el-table v-loading"loading&q…...

Redis学习笔记13:基于spring data redis及lua脚本list列表实现环形结构案例

工作过程中需要用到环形结构&#xff0c;确保环上的各个节点数据唯一&#xff0c;如果有新的不同数据到来&#xff0c;则将最早入环的数据移除&#xff0c;每次访问环形结构都自动刷新有效期&#xff1b;可以基于lua 的列表list结构来实现这一功能&#xff0c;lua脚本可以节省网…...

c# 将excel导入 sqlite

nuget 须要加载 EPPlus.Core ExcelDataReader ExcelDataReader.DataSet //需要引用的扩展 using ExcelDataReader; using ExcelPackage OfficeOpenXml.ExcelPackage; public static void CreateZhouPianChaTable(){string tbname "zhou_pian_cha1";//判断表是否存…...

KafkaConsumer 消费逻辑

版本&#xff1a;kafka-clients-2.0.1.jar 之前想写个插件修改 kafkaConsumer 消费者的逻辑&#xff0c;根据 header 过滤一些消息。于是需要了解一下 kafkaConsumer 具体是如何拉取消费消息的&#xff0c;确认在消费之前过滤掉消息是否会有影响。 下面是相关的源码&#xff0…...

scss 实用教程

变量 $ 定义变量 $link-color: blue;变量名可以与css中的属性名和选择器名称相同 使用变量 a {color: $link_color; }$highlight-border: 1px solid $link_color;中划线和下划线相互兼容&#xff0c;即中划线声明的变量可以使用下划线的方式引用&#xff0c;反之亦然。 $li…...

NO.304 二维区域和检索 - 矩阵不可变

题目 给定一个二维矩阵 matrix&#xff0c;以下类型的多个请求&#xff1a; 计算其子矩形范围内元素的总和&#xff0c;该子矩阵的 左上角 为 (row1, col1) &#xff0c;右下角 为 (row2, col2) 。 实现 NumMatrix 类&#xff1a; NumMatrix(int[][] matrix) 给定整数矩阵 …...

牛客---简单密码python

现在有一种密码变换算法。 九键手机键盘上的数字与字母的对应&#xff1a; 1--1&#xff0c; abc--2, def--3, ghi--4, jkl--5, mno--6, pqrs--7, tuv--8 wxyz--9, 0--0&#xff0c;把密码中出现的小写字母都变成九键键盘对应的数字&#xff0c;如&#xff1a;a 变成 2&#x…...

devops完整搭建教程(gitlab、jenkins、harbor、docker)

devops完整搭建教程&#xff08;gitlab、jenkins、harbor、docker&#xff09; 文章目录 devops完整搭建教程&#xff08;gitlab、jenkins、harbor、docker&#xff09;1.简介&#xff1a;2.工作流程&#xff1a;3.优缺点4.环境说明5.部署前准备工作5.1.所有主机永久关闭防火墙…...

页面上时间显示为数字 后端返回给前端 response java系统

有时候&#xff0c;在一个系统里&#xff0c;会看到&#xff0c;有的页面时间显示正常&#xff0c;有的页面时间显示成数字。像这样&#xff1a; "createTime": 1698706491000 这是因为出参没有做转换&#xff0c;直接将java.util.Date类型的数据返回给前端了。 返…...

在软件开发中正确使用MySQL日期时间类型的深度解析

在日常软件开发场景中&#xff0c;时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志&#xff0c;到供应链系统的物流节点时间戳&#xff0c;时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库&#xff0c;其日期时间类型的…...

基于ASP.NET+ SQL Server实现(Web)医院信息管理系统

医院信息管理系统 1. 课程设计内容 在 visual studio 2017 平台上&#xff0c;开发一个“医院信息管理系统”Web 程序。 2. 课程设计目的 综合运用 c#.net 知识&#xff0c;在 vs 2017 平台上&#xff0c;进行 ASP.NET 应用程序和简易网站的开发&#xff1b;初步熟悉开发一…...

HBuilderX安装(uni-app和小程序开发)

下载HBuilderX 访问官方网站&#xff1a;https://www.dcloud.io/hbuilderx.html 根据您的操作系统选择合适版本&#xff1a; Windows版&#xff08;推荐下载标准版&#xff09; Windows系统安装步骤 运行安装程序&#xff1a; 双击下载的.exe安装文件 如果出现安全提示&…...

【决胜公务员考试】求职OMG——见面课测验1

2025最新版&#xff01;&#xff01;&#xff01;6.8截至答题&#xff0c;大家注意呀&#xff01; 博主码字不易点个关注吧,祝期末顺利~~ 1.单选题(2分) 下列说法错误的是:&#xff08; B &#xff09; A.选调生属于公务员系统 B.公务员属于事业编 C.选调生有基层锻炼的要求 D…...

C++ 求圆面积的程序(Program to find area of a circle)

给定半径r&#xff0c;求圆的面积。圆的面积应精确到小数点后5位。 例子&#xff1a; 输入&#xff1a;r 5 输出&#xff1a;78.53982 解释&#xff1a;由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982&#xff0c;因为我们只保留小数点后 5 位数字。 输…...

tree 树组件大数据卡顿问题优化

问题背景 项目中有用到树组件用来做文件目录&#xff0c;但是由于这个树组件的节点越来越多&#xff0c;导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多&#xff0c;导致的浏览器卡顿&#xff0c;这里很明显就需要用到虚拟列表的技术&…...

Linux 内存管理实战精讲:核心原理与面试常考点全解析

Linux 内存管理实战精讲&#xff1a;核心原理与面试常考点全解析 Linux 内核内存管理是系统设计中最复杂但也最核心的模块之一。它不仅支撑着虚拟内存机制、物理内存分配、进程隔离与资源复用&#xff0c;还直接决定系统运行的性能与稳定性。无论你是嵌入式开发者、内核调试工…...

苹果AI眼镜:从“工具”到“社交姿态”的范式革命——重新定义AI交互入口的未来机会

在2025年的AI硬件浪潮中,苹果AI眼镜(Apple Glasses)正在引发一场关于“人机交互形态”的深度思考。它并非简单地替代AirPods或Apple Watch,而是开辟了一个全新的、日常可接受的AI入口。其核心价值不在于功能的堆叠,而在于如何通过形态设计打破社交壁垒,成为用户“全天佩戴…...

C++_哈希表

本篇文章是对C学习的哈希表部分的学习分享 相信一定会对你有所帮助~ 那咱们废话不多说&#xff0c;直接开始吧&#xff01; 一、基础概念 1. 哈希核心思想&#xff1a; 哈希函数的作用&#xff1a;通过此函数建立一个Key与存储位置之间的映射关系。理想目标&#xff1a;实现…...

用递归算法解锁「子集」问题 —— LeetCode 78题解析

文章目录 一、题目介绍二、递归思路详解&#xff1a;从决策树开始理解三、解法一&#xff1a;二叉决策树 DFS四、解法二&#xff1a;组合式回溯写法&#xff08;推荐&#xff09;五、解法对比 递归算法是编程中一种非常强大且常见的思想&#xff0c;它能够优雅地解决很多复杂的…...