当前位置: 首页 > 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></>)
}
http://www.lryc.cn/news/226697.html

相关文章:

  • Cordova系列之化繁为简:打造全场景适用的Cordova组件
  • Flink之Catalog
  • 计算机网络——物理层-传输方式(串行传输、并行传输,同步传输、异步传输,单工、半双工和全双工通信)
  • 男科医院服务预约小程序的作用是什么
  • 有没有实时检测微信聊天图片的软件,只要微信收到了有二维码的图片就把它提取出来?
  • core-site.xml,yarn-site.xml,hdfs-site.xml,mapred-site.xml配置
  • 数据分析实战 | KNN算法——病例自动诊断分析
  • JS实现数据结构与算法
  • 计算机毕业设计 基于SpringBoot的驾校管理系统的设计与实现 Java实战项目 附源码+文档+视频讲解
  • S7-1200PLC和SMART PLC开放式以太网通信(UDP双向通信)
  • 作用域插槽slot-scope
  • Redis学习笔记13:基于spring data redis及lua脚本list列表实现环形结构案例
  • c# 将excel导入 sqlite
  • KafkaConsumer 消费逻辑
  • scss 实用教程
  • NO.304 二维区域和检索 - 矩阵不可变
  • 牛客---简单密码python
  • devops完整搭建教程(gitlab、jenkins、harbor、docker)
  • 页面上时间显示为数字 后端返回给前端 response java系统
  • idea怎么配置tomcat
  • GoLong的学习之路(二十三)进阶,语法之并发(go最重要的特点)(锁,sync包,原子操作)
  • asp.net core 生命周期
  • Leetcode刷题详解—— 目标和
  • 学习c#的第六天
  • 第七章 :Spring Boot web开发常用注解(二)
  • IOC - Google Guice
  • 国际阿里云:Linux实例负载高问题排查和异常处理!!!
  • 【数据结构】二叉树的遍历递归算法详解
  • 百度王颖:百度文库以AI创作能力突破语言边界,促进思想碰撞和文化融通
  • 人工智能基础_机器学习023_理解套索回归_认识L1正则---人工智能工作笔记0063