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

效果

相关推荐
折果43 分钟前
如何在vue项目中封装自己的全局message组件?一步教会你!
前端·面试
不死鸟.亚历山大.狼崽子1 小时前
Syntax Error: Error: PostCSS received undefined instead of CSS string
前端·css·postcss
汪子熙1 小时前
Vite 极速时代的构建范式
前端·javascript
跟橙姐学代码1 小时前
一文读懂 Python 的 JSON 模块:从零到高手的进阶之路
前端·python
前端小巷子1 小时前
Vue3的渲染秘密:从同步批处理到异步微任务
前端·vue.js·面试
nightunderblackcat2 小时前
新手向:用FastAPI快速构建高性能Web服务
前端·fastapi
小码编匠2 小时前
物联网数据大屏开发效率翻倍:Vue + DataV + ECharts 的标准化模板库
前端·vue.js·echarts
欧阳天风3 小时前
分段渲染加载页面
前端·fcp
艾小码3 小时前
TypeScript在前端的实践:类型系统助力大型应用开发
前端·typescript
今禾3 小时前
前端工程化的范式革命:从 Webpack 的“全量打包”到 Vite 的“按需编译”
前端·webpack·vite