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

效果

相关推荐
qq_5470261795 小时前
Flowable 工作流引擎
java·服务器·前端
刘逸潇20056 小时前
CSS基础语法
前端·css
吃饺子不吃馅7 小时前
[开源] 从零到一打造在线 PPT 编辑器:React + Zustand + Zundo
前端·svg·图形学
小马哥编程8 小时前
【软考架构】案例分析-Web应用设计(应用服务器概念)
前端·架构
鱼与宇8 小时前
苍穹外卖-VUE
前端·javascript·vue.js
啃火龙果的兔子8 小时前
前端直接渲染Markdown
前端
z-robot8 小时前
Nginx 配置代理
前端
mapbar_front8 小时前
React 中 useCallback 的基本使用和原理解析
react.js
用户47949283569158 小时前
Safari 中文输入法的诡异 Bug:为什么输入 @ 会变成 @@? ## 开头 做 @ 提及功能的时候,测试同学用 Safari 测出了个奇怪的问题
前端·javascript·浏览器
没有故事、有酒8 小时前
Ajax介绍
前端·ajax·okhttp