React封装倒计时按钮

背景

在开发过程中,经常需要使用到倒计时的场景,当用户点击后,按钮进行倒计时,然后等待邮件或者短信发送,每次都写重复代码,会让代码显得臃肿,所以封装一个组件来减少耦合

创建一个倒计时组件

编辑基本框架

设计3个参数,一个是倒计时时长,一个是开始时执行的方法,一个是展示文本

typescript 复制代码
import React, { useState, useEffect, useRef } from 'react';
import { Button } from 'antd';

// 定义 CountdownButton 的属性接口
interface CountdownButtonProps extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'onClick'> {
    countdownTime?: number;
    text?: string;
    onStart?: () => void;
}

const CountdownButton: React.FC<CountdownButtonProps> = ({ countdownTime = 60, text = '获取验证码', onStart, ...restProps }) => {
    const [isDisabled, setIsDisabled] = useState(false);
    const [buttonText, setButtonText] = useState(text);

    // 使用useRef来保存倒计时的当前值,避免状态重置
    const countdownRef = useRef(countdownTime);

    const intervalRef = useRef<number | null>(null);

    return (
        <Button >
            {buttonText}
        </Button>
    );
};

export default CountdownButton;

实现倒计时方法

实现剩余时间修改方法

typescript 复制代码
    // 使用自定义的setCountdownRef函数来更新倒计时值
    const setCountdownRef = (update: (current: number) => number) => {
        const newCountdown = update(countdownRef.current);
        countdownRef.current = newCountdown;
    };

实现开启倒计时方法

typescript 复制代码
   const handleStartCountdown = () => {

        // 立即更新按钮文本和状态
        setButtonText(`${countdownRef.current}s后重试`);
        setIsDisabled(true);
        if (typeof onStart === 'function') {
            onStart();
        }
        // 如果已经有定时器存在,则清除它
        if (intervalRef.current !== null) {
            clearInterval(intervalRef.current!);
        }

        intervalRef.current = setInterval(() => {
            setButtonText(`${countdownRef.current}s后重试`);
            setCountdownRef((prevCountdown) => {
                if (prevCountdown <= 1) {
                    clearInterval(intervalRef.current!);
                    intervalRef.current = null;
                    setButtonText(text);
                    setIsDisabled(false);
                    return countdownTime; // 重置倒计时时间
                }
                return prevCountdown - 1;
            });
        }, 1000);

实现清楚定时器方法

typescript 复制代码
    // 清除定时器
    useEffect(() => {
        return () => {
            if (intervalRef.current !== null) {
                clearInterval(intervalRef.current!);
            }
        };
    }, []);

完整代码

typescript 复制代码
import React, { useState, useEffect, useRef } from 'react';
import { Button } from 'antd';

// 定义 CountdownButton 的属性接口
interface CountdownButtonProps extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'onClick'> {
    countdownTime?: number;
    text?: string;
    onStart?: () => void;
}

const CountdownButton: React.FC<CountdownButtonProps> = ({ countdownTime = 60, text = '获取验证码', onStart, ...restProps }) => {
    const [isDisabled, setIsDisabled] = useState(false);
    const [buttonText, setButtonText] = useState(text);

    // 使用useRef来保存倒计时的当前值,避免状态重置
    const countdownRef = useRef(countdownTime);

    const intervalRef = useRef<number | null>(null);

    // 清除定时器
    useEffect(() => {
        return () => {
            if (intervalRef.current !== null) {
                clearInterval(intervalRef.current!);
            }
        };
    }, []);

    const handleStartCountdown = () => {

        // 立即更新按钮文本和状态
        setButtonText(`${countdownRef.current}s后重试`);
        setIsDisabled(true);
        if (typeof onStart === 'function') {
            onStart();
        }
        // 如果已经有定时器存在,则清除它
        if (intervalRef.current !== null) {
            clearInterval(intervalRef.current!);
        }

        intervalRef.current = setInterval(() => {
            setButtonText(`${countdownRef.current}s后重试`);
            setCountdownRef((prevCountdown) => {
                if (prevCountdown <= 1) {
                    clearInterval(intervalRef.current!);
                    intervalRef.current = null;
                    setButtonText(text);
                    setIsDisabled(false);
                    return countdownTime; // 重置倒计时时间
                }
                return prevCountdown - 1;
            });
        }, 1000);

        // 立即减少一次倒计时,使首次显示正确的剩余时间
        setCountdownRef((prevCountdown) => prevCountdown - 1);
    };

    // 使用自定义的setCountdownRef函数来更新倒计时值
    const setCountdownRef = (update: (current: number) => number) => {
        const newCountdown = update(countdownRef.current);
        countdownRef.current = newCountdown;
    };

    return (
        <Button {...restProps} onClick={handleStartCountdown} disabled={isDisabled}>
            {buttonText}
        </Button>
    );
};

export default CountdownButton;

使用方法

html 复制代码
<CountdownButton countdownTime={60} text={"获取验证码"} onStart={sendMsg} type="primary" />

效果

相关推荐
冷冷的菜哥21 分钟前
react多文件分片上传——支持拖拽与进度展示
前端·react.js·typescript·多文件上传·分片上传
玄魂35 分钟前
VChart 官网上线 智能助手与分享功能
前端·llm·数据可视化
wyzqhhhh1 小时前
插槽vue/react
javascript·vue.js·react.js
许___1 小时前
Vue使用原生方式把视频当作背景
前端·javascript·vue.js
萌萌哒草头将军1 小时前
尤雨溪强烈推荐的这个库你一定要知道 ⚡️⚡️⚡️
前端·vue.js·vite
2401_878454531 小时前
Vue 核心特性详解:计算属性、监听属性与事件交互实战指南
前端·vue.js·交互
1024小神2 小时前
uniapp+vue3+vite+ts+xr-frame实现ar+vr渲染踩坑记
前端
测试界清流2 小时前
基于pytest的接口测试
前端·servlet
知识分享小能手3 小时前
微信小程序入门学习教程,从入门到精通,自定义组件与第三方 UI 组件库(以 Vant Weapp 为例) (16)
前端·学习·ui·微信小程序·小程序·vue·编程
trsoliu3 小时前
多仓库 Workspace 协作机制完整方案
前端