React封装倒计时按钮
背景
在开发过程中,经常需要使用到倒计时的场景,当用户点击后,按钮进行倒计时,然后等待邮件或者短信发送,每次都写重复代码,会让代码显得臃肿,所以封装一个组件来减少耦合
创建一个倒计时组件
编辑基本框架
设计3个参数,一个是倒计时时长,一个是开始时执行的方法,一个是展示文本
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;
实现倒计时方法
实现剩余时间修改方法
// 使用自定义的setCountdownRef函数来更新倒计时值
const setCountdownRef = (update: (current: number) => number) => {
const newCountdown = update(countdownRef.current);
countdownRef.current = newCountdown;
};
实现开启倒计时方法
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);
实现清楚定时器方法
// 清除定时器
useEffect(() => {
return () => {
if (intervalRef.current !== null) {
clearInterval(intervalRef.current!);
}
};
}, []);
完整代码
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;
使用方法
<CountdownButton countdownTime={60} text={"获取验证码"} onStart={sendMsg} type="primary" />