开发一个 React Loading 组件

妙玉
• 阅读 7230
一直都是用的第三方库的loading组件(ant-design/antd-mobil),最近一个项目需要用到loading,自己开发了下,总结如下:

Loading组件的两种形式:全局loading局部loading

  • 全局loading:一般覆盖全屏居中显示,这种情况的调用方式一般是编程式调用即 Loading.show()
  • 局部loading:只对某个区块进行loading,这种情况一般是组件包裹形式使用

    // 使用方式同 Spin 组件
    <Spin visible={true}>
        <div>区块内容</div>
    </Spin>
  • 两种情况都做了请求速度过快时的防闪烁处理

意外收获 portals

  • 在开发全局loading时,再想怎么把loading组件挂在body顶层时,用了 ReactDom.render 这个方法一般是一些脚手架帮我们生成项目代码时自动生成的把顶层App组件放在root节点上时才会用到,这里我们利用它把 loading组件挂在我们指定的顶层dom节点上
  • ReactDOM.createPortal(child, container) 官方文档知乎学习文章

    portals这个东西就是可以把组件放到指定的dom节点,而使用时依旧可以像普通使用组件那样使用,只不过生成的dom树不是在一起的
    
    官方话术:Portal 提供了一种将子节点渲染到存在于父组件以外的 DOM 节点的优秀的方案
    
    而且支持合成事件冒泡
    
    利用这个方法开发一些 Dialog、Modal 组件就非常方便、非常简洁了
    antd的 Modal 组件也是用的这个方法

两种loading代码如下

  • 全局loading,这里用到了 ReactDom.render(<Comps/>, domNode) 这个顶层api

    import React from 'react';
    import ReactDOM from 'react-dom';
    import './style/loading';
    
    class Loading {
        domNode: HTMLElement
        isExistNode: boolean
        timer: any
        constructor() {
            this.domNode = document.createElement('div');
            this.isExistNode = false;
        }
    
        private render(visible: boolean) {
            if (!this.isExistNode && visible) {
                document.body.appendChild(this.domNode);
                const children = this.createNode();
                ReactDOM.render(children, this.domNode);
                this.isExistNode = true
            }
            if (visible) {
                this.domNode.className = 'hp-loading-wrap';
            } else {
                this.domNode.className = 'hp-loading-wrap hide';
                // ReactDOM.unmountComponentAtNode(this.domNode)
            }
        } 
        createNode() {
            const node = <div className="loading-content"><div className="loading-img"></div></div>;
            return node;
        }
    
        show(isDelay=true, delay=300) {
            this.timer && clearTimeout(this.timer)
            if (!isDelay) {
                this.render(true);
            } else {
                // 防闪烁
                this.timer = setTimeout(() => this.render(true), delay);
            }
        }
    
        hide() {
            this.timer && clearTimeout(this.timer)
            this.render(false)
        }
    }
    
    export default new Loading()
    
    // 样式
    .hp-loading-wrap {
        position: fixed;
        z-index: 99999;
        width: 100%;
        height: 100%;
        top: 0;
        left: 0;
        display: flex;
        align-items: center;
        &.hide {
            display: none;
        }
        .loading-content {
            width: 37.5px;/*no*/
            height: 18px;/*no*/
            margin: 0 auto;
            text-align: center;
        }
        .loading-img {
            width: 100%;
            height: 100%;
            margin: 0 auto;
            background-repeat: no-repeat;
            background-size: contain;
            background-position: center;
            background-image: url(../image/loading.gif)
        }
    }
  • 局部loading,这里用了hooks,使用方式同 antd Spin 组件

    import React, { useState, useEffect } from "react";
    import './style/spin.less'
    
    interface propsType {
        visible: boolean;
        children?: any;
        tips?: any; 
        delay?: number;
    }
    
    let timer:any;
    export default function Spin(props: propsType) {
        const [visible, setVisible] = useState(props.visible);
    
        useEffect(() => {
            if (props.delay) { // 防闪烁
                timer && clearTimeout(timer);
                if (props.visible) {
                    timer = setTimeout(() => setVisible(true), props.delay);
                } else {
                    setVisible(false);
                }
            } else {
                setVisible(props.visible);
            }
        }, [props.visible]);
        return (
            <div className={visible ? "hp-spin" : "hp-spin hide"}>
                {props.children}
                <div className={props.children ? "spin-content" : ''}>
                    <div className="spin-img"></div>
                </div>
            </div>
        );
    }
    
    // 样式
    .hp-spin {
        position: relative;
        .spin-content {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            z-index: 999;
        }
        &.hide .spin-content {
            display: none;
        }
        .spin-img {
            display: inline-block;
            width: 37.5px;/*no*/
            height: 18px;/*no*/
            background-repeat: no-repeat;
            background-size: contain;
            background-image: url('../image/loading.gif');
        }
    }
    
点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
美凌格栋栋酱 美凌格栋栋酱
6个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Jacquelyn38 Jacquelyn38
4年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Karen110 Karen110
3年前
​一篇文章总结一下Python库中关于时间的常见操作
前言本次来总结一下关于Python时间的相关操作,有一个有趣的问题。如果你的业务用不到时间相关的操作,你的业务基本上会一直用不到。但是如果你的业务一旦用到了时间操作,你就会发现,淦,到处都是时间操作。。。所以思来想去,还是总结一下吧,本次会采用类型注解方式。time包importtime时间戳从1970年1月1日00:00:00标准时区诞生到现在
徐小夕 徐小夕
4年前
《精通react/vue组件设计》之5分钟教你实现一个极具创意的加载(Loading)组件
前言本文是笔者写组件设计的第八篇文章,今天带大家用5分钟实现一个极具创意的加载(loading)组件.涉及的核心知识点主要是css3相关特性,如果大家非常熟悉,可直接跳过介绍直接看正文.时刻问自己:是否具备创造力?笔记前端组件的一般分类:通用型组件:比如Button,Icon等.布局型组件:比如Grid,Layout布
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Stella981 Stella981
3年前
React Native第三方组件和示例链接
以下是ReactNative的链接,有需要第三方组件或者示例的小伙伴可以收藏一下01、ReactNative之TabView:https://js.coach/reactnative/reactnativetabview02、ReactNative之正在加载Loading条:https://js.coach/reactnative/
Python进阶者 Python进阶者
1年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这