【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:plus 和 minus,即递增和递减。
此外,我还为 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(加载)的效果十分常见,在某些场景下,一个页面上甚至可能有特别多的 loading 存在,此时为每一个 loading 专门创建一个 state 显然太过繁琐,不如试试写一个 useLoading 来集中管理ÿ…...
Cordova系列之化繁为简:打造全场景适用的Cordova组件
前言 在我之前的文章 Cordova初探 的开篇中说到了Cordova在Android应用开发中的一个显著的局限性就是我们的Activity必须继承其提供的CordovaActivity。这种设计对于那些追求个性化UI设计的项目而言,显得尤为受限。 其实也可以理解,Cordova主要旨在为前…...
Flink之Catalog
Catalog Catalog概述Catalog分类 GenericInMemoryCatalogJdbcCatalog下载JAR包及使用重启操作创建Catalog查看与使用Catalog自动初始化catalog HiveCatalog下载JAR包及使用重启操作hive metastore服务创建Catalog查看与使用CatalogFlink与Hive中操作自动初始化catalog 用户自定…...
计算机网络——物理层-传输方式(串行传输、并行传输,同步传输、异步传输,单工、半双工和全双工通信)
目录 串行传输和并行传输 同步传输和异步传输 单工、半双工和全双工通信 串行传输和并行传输 串行传输是指数据是一个比特一个比特依次发送的。因此在发送端和接收端之间,只需要一条数据传输线路即可。 并行传输是指一次发送n个比特,而不是一个比特&…...
男科医院服务预约小程序的作用是什么
医院的需求度从来都很高,随着技术发展,不少科目随之衍生出新的医院的,比如男科医院、妇科医院等,这使得目标群体更加精准,同时也赋能用户可以快速享受到服务。 当然相应的男科医院在实际经营中也面临痛点:…...
有没有实时检测微信聊天图片的软件,只要微信收到了有二维码的图片就把它提取出来?
10-2 如果你有需要自动并且快速地把微信收到的二维码图片保存到指定文件夹的需求,那本文章非常适合你,本文章教你如何实现自动保存微信收到的二维码图片到你指定的文件夹中,助你快速扫码,比别人领先一步。 首先需要准备好的材料…...
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” 数据集链接: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实战项目 附源码+文档+视频讲解
博主介绍:✌从事软件开发10年之余,专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ 🍅文末获取源码联系🍅 👇🏻 精…...
S7-1200PLC和SMART PLC开放式以太网通信(UDP双向通信)
S7-1200PLC的以太网通信UDP通信相关介绍还可以参考下面文章链接: 博途PLC开放式以太网通信TRCV_C指令应用编程(运动传感器UDP通信)-CSDN博客文章浏览阅读2.8k次。博途PLC开放式以太网通信TSENG_C指令应用,请参看下面的文章链接:博途PLC 1200/1500PLC开放式以太网通信TSEND_…...
作用域插槽slot-scope
一般用于组件封装,将使用props传入组件的数据再次调出来或者单纯调用组件中的数据。也可用于为组件某个部分自定义样式以及为某次使用组件自定义样式。 直接拿elementui的el-table举例: <template><el-table v-loading"loading&q…...
Redis学习笔记13:基于spring data redis及lua脚本list列表实现环形结构案例
工作过程中需要用到环形结构,确保环上的各个节点数据唯一,如果有新的不同数据到来,则将最早入环的数据移除,每次访问环形结构都自动刷新有效期;可以基于lua 的列表list结构来实现这一功能,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 消费逻辑
版本:kafka-clients-2.0.1.jar 之前想写个插件修改 kafkaConsumer 消费者的逻辑,根据 header 过滤一些消息。于是需要了解一下 kafkaConsumer 具体是如何拉取消费消息的,确认在消费之前过滤掉消息是否会有影响。 下面是相关的源码࿰…...
scss 实用教程
变量 $ 定义变量 $link-color: blue;变量名可以与css中的属性名和选择器名称相同 使用变量 a {color: $link_color; }$highlight-border: 1px solid $link_color;中划线和下划线相互兼容,即中划线声明的变量可以使用下划线的方式引用,反之亦然。 $li…...
NO.304 二维区域和检索 - 矩阵不可变
题目 给定一个二维矩阵 matrix,以下类型的多个请求: 计算其子矩形范围内元素的总和,该子矩阵的 左上角 为 (row1, col1) ,右下角 为 (row2, col2) 。 实现 NumMatrix 类: NumMatrix(int[][] matrix) 给定整数矩阵 …...
牛客---简单密码python
现在有一种密码变换算法。 九键手机键盘上的数字与字母的对应: 1--1, abc--2, def--3, ghi--4, jkl--5, mno--6, pqrs--7, tuv--8 wxyz--9, 0--0,把密码中出现的小写字母都变成九键键盘对应的数字,如:a 变成 2&#x…...
devops完整搭建教程(gitlab、jenkins、harbor、docker)
devops完整搭建教程(gitlab、jenkins、harbor、docker) 文章目录 devops完整搭建教程(gitlab、jenkins、harbor、docker)1.简介:2.工作流程:3.优缺点4.环境说明5.部署前准备工作5.1.所有主机永久关闭防火墙…...
页面上时间显示为数字 后端返回给前端 response java系统
有时候,在一个系统里,会看到,有的页面时间显示正常,有的页面时间显示成数字。像这样: "createTime": 1698706491000 这是因为出参没有做转换,直接将java.util.Date类型的数据返回给前端了。 返…...
挑战杯推荐项目
“人工智能”创意赛 - 智能艺术创作助手:借助大模型技术,开发能根据用户输入的主题、风格等要求,生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用,帮助艺术家和创意爱好者激发创意、提高创作效率。 - 个性化梦境…...
Oracle查询表空间大小
1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...
Java 加密常用的各种算法及其选择
在数字化时代,数据安全至关重要,Java 作为广泛应用的编程语言,提供了丰富的加密算法来保障数据的保密性、完整性和真实性。了解这些常用加密算法及其适用场景,有助于开发者在不同的业务需求中做出正确的选择。 一、对称加密算法…...
使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台
🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...
鸿蒙DevEco Studio HarmonyOS 5跑酷小游戏实现指南
1. 项目概述 本跑酷小游戏基于鸿蒙HarmonyOS 5开发,使用DevEco Studio作为开发工具,采用Java语言实现,包含角色控制、障碍物生成和分数计算系统。 2. 项目结构 /src/main/java/com/example/runner/├── MainAbilitySlice.java // 主界…...
Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信
文章目录 Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket(服务端和客户端都要)2. 绑定本地地址和端口&#x…...
Python Ovito统计金刚石结构数量
大家好,我是小马老师。 本文介绍python ovito方法统计金刚石结构的方法。 Ovito Identify diamond structure命令可以识别和统计金刚石结构,但是无法直接输出结构的变化情况。 本文使用python调用ovito包的方法,可以持续统计各步的金刚石结构,具体代码如下: from ovito…...
jmeter聚合报告中参数详解
sample、average、min、max、90%line、95%line,99%line、Error错误率、吞吐量Thoughput、KB/sec每秒传输的数据量 sample(样本数) 表示测试中发送的请求数量,即测试执行了多少次请求。 单位,以个或者次数表示。 示例:…...
通过MicroSip配置自己的freeswitch服务器进行调试记录
之前用docker安装的freeswitch的,启动是正常的, 但用下面的Microsip连接不上 主要原因有可能一下几个 1、通过下面命令可以看 [rootlocalhost default]# docker exec -it freeswitch fs_cli -x "sofia status profile internal"Name …...
SpringAI实战:ChatModel智能对话全解
一、引言:Spring AI 与 Chat Model 的核心价值 🚀 在 Java 生态中集成大模型能力,Spring AI 提供了高效的解决方案 🤖。其中 Chat Model 作为核心交互组件,通过标准化接口简化了与大语言模型(LLM࿰…...
