用React实现一个秒杀倒计时组件

复制代码
先声明一个补0的函数
复制代码
// 先声明一个补0的函数
function formate(time: number): string {
  return `${time < 10 ? "0" : ""}${time}`;
}
复制代码
定义最终返回的数据结构
复制代码
export interface TimeInfo {
  /** 天 */
  day: number;
  /** 小时 */
  hours: number;
  /** 补零后的小时 */
  hoursStr: string;
  /** 分钟 */
  minutes: number;
  /** 补零后的分 */
  minutesStr: string;
  /** 秒 */
  seconds: number;
  /** 补零后的秒 */
  secondsStr: string;
  /** 毫秒 */
  milliseconds?: number;
  /** 补零后的毫秒 */
  millisecondsStr?: string;
  /** 倒计时状态 pending-初始化状态 runing-进行中 end-已结束 */
  status: "pending" | "runing" | "end";
}
复制代码
工具方法——清除倒计时数据信息:
复制代码
function clearCountdownInfo(showMillisecond = false, status?: TimeInfo["status"]): TimeInfo {
  const timeInfo: TimeInfo = {
    day: 0,
    hours: 0,
    hoursStr: "00",
    minutes: 0,
    minutesStr: "00",
    seconds: 0,
    secondsStr: "00",
    status: status || "end",
  };

  if (showMillisecond) {
    timeInfo.milliseconds = 0;
    timeInfo.millisecondsStr = "0";
  }

  return timeInfo;
}
复制代码
关键工具方法——计算倒计时返回的数据信息:
复制代码
function computeCountdownInfo(
  remainTime: number,
  showMillisecond = false
): TimeInfo {
  // 剩余时间小于说明结束,直接清空
  if (remainTime < 0) {
    return clearCountdownInfo(showMillisecond);
  }

  // 这里用了一个比较笨的方法,一个个进行计算,后续可以优化试试看
  const day = Math.floor(remainTime / (24 * 60 * 60));
  const hours = Math.floor((remainTime / (60 * 60)) % 24);
  const hoursStr = formate(hours);
  const minutes = Math.floor((remainTime / 60) % 60);
  const minutesStr = formate(minutes);
  const seconds = Math.floor(remainTime % 60);
  const secondsStr = formate(seconds);

  // 组合成需要返回的时间信息
  const timeInfo: TimeInfo = {
    day,
    hours,
    hoursStr,
    minutes,
    minutesStr,
    seconds,
    secondsStr,
    status: "runing",
  };

  // 需要显示毫秒逻辑处理
  if (showMillisecond) {
    const milliseconds = Math.floor(remainTime * 1000);
    // 只取首位
    const millisecondsStr = String(milliseconds).slice(-3);
    timeInfo.milliseconds = milliseconds;
    timeInfo.millisecondsStr = millisecondsStr;
  }

  return timeInfo;
}

定义函数:

复制代码
interface CountDownOptions {
  showMillisecond: boolean
  deadlineTime: any
}

function computeRemainTime(deadlineTime: number) {
  // 当前时间
  const nowTime = Date.now();
  // 截止时间 - 当前时间 = 剩余时间
  const remainTime = (deadlineTime - nowTime) / 1000;
  return remainTime;
}
复制代码
核心逻辑 —— useCountdown hook 组件:
复制代码
export const useCountdown = (options: CountDownOptions) => {
  // 首次初始化数据,显示清除的数据
  const [timeInfo, setTimeInfo] = useState<TimeInfo>(
    clearCountdownInfo(options.showMillisecond, "pending")
  );
  useEffect(() => {
    let timer = 0;

    function countdown() {
      const remainTime = computeRemainTime(options.deadlineTime);
      // 剩余时间大于 0 才开始倒计时
      if (remainTime > 0) {
        // 未结束时直接定时下一次在执行判断 countdown
        timer = setTimeout(
          countdown,
          options.showMillisecond ? 100 : 1000 // 毫秒级则修改定时器时间
        );
      }
      const data = computeCountdownInfo(remainTime, options.showMillisecond);
      setTimeInfo(data);
    }

    // 开始倒计时
    countdown();

    return () => {
      // 清除定时器
      timer && clearTimeout(timer);
    };
  }, [options.deadlineTime, options.showMillisecond]);

  return timeInfo;
};

看一下在组件中的应用:

复制代码
import { Layout, Row, Typography } from 'antd';
import React from 'react';
import { useCountdown } from './CountDown';

interface Props {
  name: string;
}

const Guide: React.FC<Props> = (props) => {

  // deadlineTime这个时间在传的时候要比当前的时间往后,要不然效果出不来,这里注意一点
  const {
    day,
    hoursStr,
    minutesStr,
    secondsStr,
  } = useCountdown({showMillisecond: false, deadlineTime: 1743907169000})

  return (
    <Layout>
      <div style={{color: 'red', fontSize: 28, marginBottom: 24}}>
        {day}天:
        {hoursStr}时:
        {minutesStr}分:
        {secondsStr}秒
      </div>
    </Layout>
  );
};

export default Guide;

再来看一下页面的效果吧:

相关推荐
风无雨11 分钟前
react antd 项目报错Warning: Each child in a list should have a unique “key“prop
前端·react.js·前端框架
人无远虑必有近忧!12 分钟前
video标签播放mp4格式视频只有声音没有图像的问题
前端·video
记得早睡~4 小时前
leetcode51-N皇后
javascript·算法·leetcode·typescript
安分小尧5 小时前
React 文件上传新玩法:Aliyun OSS 加持的智能上传组件
前端·react.js·前端框架
编程社区管理员5 小时前
React安装使用教程
前端·react.js·前端框架
拉不动的猪5 小时前
vue自定义指令的几个注意点
前端·javascript·vue.js
yanyu-yaya6 小时前
react redux的学习,单个reducer
前端·javascript·react.js
skywalk81636 小时前
OpenRouter开源的AI大模型路由工具,统一API调用
服务器·前端·人工智能·openrouter
Liudef066 小时前
deepseek v3-0324 Markdown 编辑器 HTML
前端·编辑器·html·deepseek
拉不动的猪6 小时前
uniapp与React Native/vue 的简单对比
前端·vue.js·面试