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" />

效果

相关推荐
Rsun045517 分钟前
React相关面试题
前端·react.js·前端框架
鹏多多.31 分钟前
Flutter使用screenshot进行截屏和截长图以及分享保存的全流程指南
android·前端·flutter·ios·前端框架
LawrenceLan1 小时前
37.Flutter 零基础入门(三十七):SnackBar 与提示信息 —— 页面反馈与用户交互必学
开发语言·前端·flutter·dart
迪巴拉15251 小时前
基于Vue与Spring Boot+Open Cv的智慧校园考勤系统
前端·vue.js·spring boot
swipe1 小时前
JavaScript 对象与属性描述符:从原理到实战
前端·javascript·面试
&活在当下&1 小时前
Vue3 h函数用法详解
前端·javascript·vue.js
小贵子的博客1 小时前
(vue3错误处理)has naming conflicts with other components, ignored.
前端·javascript·vue.js
木斯佳2 小时前
前端八股文面经大全:字节跳动音视频前端一面·下(2026-03-03)·面经深度解析
前端·音视频·状态模式