你可能不知道的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组件库的实现也是同理,只要有倒计时组件就可以实现。

总结

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

相关推荐
y先森19 分钟前
CSS3中的伸缩盒模型(弹性盒子、弹性布局)之伸缩容器、伸缩项目、主轴方向、主轴换行方式、复合属性flex-flow
前端·css·css3
前端Hardy19 分钟前
纯HTML&CSS实现3D旋转地球
前端·javascript·css·3d·html
susu108301891122 分钟前
vue3中父div设置display flex,2个子div重叠
前端·javascript·vue.js
IT女孩儿1 小时前
CSS查缺补漏(补充上一条)
前端·css
吃杠碰小鸡2 小时前
commitlint校验git提交信息
前端
虾球xz3 小时前
游戏引擎学习第20天
前端·学习·游戏引擎
我爱李星璇3 小时前
HTML常用表格与标签
前端·html
疯狂的沙粒3 小时前
如何在Vue项目中应用TypeScript?应该注意那些点?
前端·vue.js·typescript
小镇程序员3 小时前
vue2 src_Todolist全局总线事件版本
前端·javascript·vue.js
野槐3 小时前
前端图像处理(一)
前端