你可能不知道的antd倒计时组件用法

前言

ps: 本文使用ant组件库和react技术栈,因此假定你导入了这些依赖包。

如果让你实现一个显示当前日期的定时器组件,你会怎么做?如下图所示:

初步实现探索

碰到这样的需求,你是不是会使用定时器来实现,如下所示:

tsx 复制代码
import React, { useEffect, useState } from 'react';
import { Statistic } from 'antd';
import dayjs from 'dayjs';
import utc from 'dayjs/plugin/utc';
dayjs.extend(utc);

const Interval = ({ utcOffset, style = {} }) => {

const [currentTime, setCurrentTime] = useState(
    dayjs().utcOffset(utcOffset).format('YYYY 年 MM 月 DD 日 HH : mm : ss')
);

useEffect(() => {
    const interval = setInterval(() => {
        setCurrentTime(
            dayjs().utcOffset(utcOffset).format('YYYY 年 MM 月 DD 日 HH : mm : ss')
        );
    }, 1000);
    return () => clearInterval(interval);
}, []);

return (
    <div style={{ ...style, display: 'flex', justifyContent: 'center' }}>
        <Statistic value={currentTime} valueStyle={{ fontSize: '18px' }} />
        <span 
            style={{ 
                fontSize: '18px', 
                color:'rgb(203,207,212)',
                paddingLeft:'20px'
              }}
        >UTC{utcOffset}</span>
    </div>
);
};

export default Interval;

可以看到,我们使用到了useEffect,在里面使用setInterval方法来不停的设置值,从而达到实现定时器组件,但其实我们没有必要这样去实现。

那我们应该如何去实现这样一个组件呢,假如项目当中引入了ahooks,也许还会有人想我们可以使用hooks的useInterval或者自己封装实现一个useInterval方法来替代这里的setInterval。如下所示:

tsx 复制代码
import React, { useEffect, useState } from 'react';
import { Statistic } from 'antd';
import dayjs from 'dayjs';
import utc from 'dayjs/plugin/utc';
import { useInterval } from 'ahooks';
dayjs.extend(utc);

const Interval = ({ utcOffset, style = {} }) => {

const [currentTime, setCurrentTime] = useState(
    dayjs().utcOffset(utcOffset).format('YYYY 年 MM 月 DD 日 HH : mm : ss')
);

 useInterval(() => {
     setCurrentTime(
            dayjs().utcOffset(utcOffset).format('YYYY 年 MM 月 DD 日 HH : mm : ss')
        );
  }, 1000);

return (
    <div style={{ ...style, display: 'flex', justifyContent: 'center' }}>
        <Statistic value={currentTime} valueStyle={{ fontSize: '18px' }} />
        <span 
            style={{ 
                fontSize: '18px', 
                color:'rgb(203,207,212)',
                paddingLeft:'20px'
              }}
        >UTC{utcOffset}</span>
    </div>
);
};

export default Interval;

这个组件我们还可以优化,比如去掉div标签和span标签,不过最终我们实现的方式都是通过一个定时器(要么借助hooks的定时器,要么自己实现定时器)来实现。

但其实我们并不需要这么实现,我们可以基于antd的倒计时组件来实现,下面我们就来一起看下吧。

更方便的实现

也许有人会疑惑,为什么antd的倒计时组件可以实现,那我就要反问一个问题,定时器组件的核心是什么?是定时器对不对?也就是不停的执行,倒计时在限定的时间内不停的执行不就可以看作是一个定时器吗?只不过这个定时器有结束的时候。

因此,我们要想基于倒计时组件来实现,那么首先就需要实现一个没有结束的倒计时,然后基于这个没有结束的倒计时去自定义倒计时的显示值。因此核心实现就2点,总结如下:

  1. 实现一个没有结束的倒计时。
  2. 自定义倒计时组件的显示值。

实现一个没有结束的倒计时(即定时器)

那么我们如何实现一个没有结束的倒计时呢?那么我们就要去了解antd倒计时组件的api,通过api我们可以看到倒计时组件提供一个value属性,如果value属性存在一个差值,那么倒计时就会循环执行。例如:

tsx 复制代码
// 倒计时差值
const diffValue = Date.now() + 1000;
// 组件渲染
<Countdown value={diffValue} />

倒计时就会有1s的差值,然后倒计时组件就会有1s的倒计时,在这1s期间就是不停的执行,然后倒计时结束就会执行onFinish方法。

到了这里,也许有读者就会知道了,没错,我们就是定义一个状态用来管理这个差值,然后在倒计时结束的时候初始化这个差值。如下所示:

tsx 复制代码
 const [diffValue, setDiffValue] = useState(Date.now() + 1000);
 const resetCountdown = () => {
    setDiffValue(Date.now() + 1000);
 };
 const onFinish = () => {
    resetCountdown();
 };
 // 组件渲染
<Countdown
   onFinish={onFinish}
   value={diffValue}
/>

你以为到了这里就完了吗?虽然我们更改了状态,但是组件并没有更新,因此视图永远都会停留在当前日期,不会跟随着递增,因此这里我们就需要时刻更新这个组件,没错,就是给组件添加一个key值。如下所示:

tsx 复制代码
// ...
<Countdown
   onFinish={onFinish}
   value={diffValue}
   key={diffValue}
/>

如此一来,我们的定时器组件就会随着diffValue的变动而不断更新,从而达到定时器效果,第一步我们就完成了。

自定义倒计时的展示值

接下来,我们就需要自定义实现倒计时的展示,通过倒计时组件的api,我们知道有两种方式来自定义展示值。

其一,我们可以通过format来自定义展示值,不过这个展示仅仅只能格式化日期格式,并不能自定义标签什么的展示。

其二,通过观察倒计时组件源码,我们可以发现倒计时组件是基于Statistic组件来渲染的,因此我们也可以通过formatter来自定义展示值。

这里我们仅仅只是自定义展示值(根据时区来自定义展示日期时间),因此,我们采用第一种方式来实现。代码如下所示:

tsx 复制代码
const getValue = () =>
    utc ? dayjs().utcOffset(utc).format('YYYY 年 MM 月 DD 日 HH : mm : ss') : dayjs().format('YYYY年MM月DD日 HH:mm:ss');
 // 组件展示
 <Countdown
      key={count}
      onFinish={onFinish}
      value={count}
      format={getValue()}
/>

如此一来,我们就实现了一个定时器展示日期,接下来我们还需要展示时区,我们只需要指定suffix属性即可,如下所示:

tsx 复制代码
  <Countdown
      key={count}
      onFinish={onFinish}
      value={count}
      format={getValue()}
      // 自定义时区
      suffix={utc && <span style={{ marginLeft: 5 }}>{`UTC(${utc})`}</span>}
  />

接下来,我们再完善封装一下,一个定时器组件就大功告成了,如下所示:

tsx 复制代码
import { Statistic } from 'antd';
import { CountdownProps } from 'antd/lib/statistic/Countdown';
import dayjs from 'dayjs';
import utc from 'dayjs/plugin/utc';

import React, { useState } from 'react';
const { Countdown } = Statistic;

dayjs.extend(utc);

export type AntdIntervalProps = {
  countDownProps: CountdownProps;
  utc: string;
};

const AntdInterval: React.FC<Partial<AntdIntervalProps>> = (props) => {
  const { countDownProps = {}, utc = '+08:00' } = props;
  const getValue = () =>
    utc
      ? dayjs().utcOffset(utc).format('YYYY 年 MM 月 DD 日 HH : mm : ss')
      : dayjs().format('YYYY年MM月DD日 HH:mm:ss');
  const [diffValue, setDiffValue] = useState(Date.now() + 1000);
  const resetCountdown = () => {
    setDiffValue(Date.now() + 1000);
  };
  const onFinish = () => {
    resetCountdown();
  };
  return (
    <Countdown
      key={diffValue}
      onFinish={onFinish}
      value={diffValue}
      format={getValue()}
      suffix={utc && <span style={{ marginLeft: 5 }}>{`UTC(${utc})`}</span>}
      {...countDownProps}
    />
  );
};

export default AntdInterval;

可以看到我们设计了countDownProps和utc两个props,countDownProps也就是antd倒计时组件的props,而utc就是我们的时区。以下是一个最终实现版本的在线示例。(后面再补上掘金代码段地址)

在线示例

基于acro design的实现

acro design倒计时组件的api与antd有点差异,不过实现原理都是大同小异的,如下所示:

tsx 复制代码
import { Statistic, StatisticProps, CountdownProps } from '@arco-design/web-react';
import dayjs from 'dayjs';
import utc from 'dayjs/plugin/utc';

import React, { useState } from 'react';
const { Countdown } = Statistic;

dayjs.extend(utc);

export type IntervalProps = {
  countDownProps: CountdownProps;
  StatisticProps: StatisticProps;
  utc: string;
};
const Interval: React.FC<Partial<IntervalProps>> = props => {
  const { countDownProps = {}, StatisticProps = {}, utc = '+08:00' } = props;
  const getValue = () =>
    utc ? dayjs().utcOffset(utc).format('YYYY 年 MM 月 DD 日 HH : mm : ss') : dayjs().format('YYYY年MM月DD日 HH:mm:ss');
  const [count, setCount] = useState(Date.now() + 1000);
  const resetCountdown = () => {
    setCount(Date.now() + 1000);
  };
  const onFinish = () => {
    resetCountdown();
  };
  return (
    <Countdown
      key={count}
      onFinish={onFinish}
      value={count}
      renderFormat={() => <Statistic value={getValue()} suffix={utc && `UTC(${utc})`} {...StatisticProps} />}
      {...countDownProps}
    />
  );
};

export default Interval;

其它react ui组件库的实现也是同理,只要有倒计时组件就可以实现。

总结

读完本文,如果你还在采用最开始提到的实现方式来实现一个定时器组件,那就没有必要了,赶紧换成最后这种方式吧。

相关推荐
喵叔哟27 分钟前
重构代码之取消临时字段
java·前端·重构
还是大剑师兰特1 小时前
D3的竞品有哪些,D3的优势,D3和echarts的对比
前端·javascript·echarts
王解1 小时前
【深度解析】CSS工程化全攻略(1)
前端·css
一只小白菜~1 小时前
web浏览器环境下使用window.open()打开PDF文件不是预览,而是下载文件?
前端·javascript·pdf·windowopen预览pdf
方才coding1 小时前
1小时构建Vue3知识体系之vue的生命周期函数
前端·javascript·vue.js
阿征学IT1 小时前
vue过滤器初步使用
前端·javascript·vue.js
王哲晓1 小时前
第四十五章 Vue之Vuex模块化创建(module)
前端·javascript·vue.js
丶21361 小时前
【WEB】深入理解 CORS(跨域资源共享):原理、配置与常见问题
前端·架构·web
发现你走远了1 小时前
『VUE』25. 组件事件与v-model(详细图文注释)
前端·javascript·vue.js
Mr.咕咕1 小时前
Django 搭建数据管理web——商品管理
前端·python·django