点击就产生60秒倒计时的按钮
点击后就会执行某操作,立刻开启倒计时x秒,倒计时结束才能再次能够点击。
export function subscribeInterval(ms, task) {
const handle = setInterval(task, ms);
return () => clearInterval(handle);
}
export const useInterval = (ms, task) => useEffect(() => subscribeInterval(ms, task), []);
export function CountDownButton({countDown, normalText, waitText, task, ...props}) {
const [count, setCount] = useState(0);
const [isWait, setIsWait] = useState(false);
const timer = useRef(-1);
// const wait = ()=>{
// clearInterval(timer.current);
// timer.current = setInterval(()=>{
// setCount(count => count -1);
// if(count <= 0){
// setIsWait(false);
// clearInterval(timer.current);
// setCount(countDown);
// console.log(count);
// console.log("??");
// }
// }, 1000);
// };
useEffect(()=>{
clearInterval(timer.current);
timer.current = setInterval(()=>{
setCount(count => count -1);
console.log(count)
if(count == 0){
setIsWait(false);
clearInterval(timer.current);
console.log(count);
console.log("??");
}
}, 1000);
}, [count]);
// useEffect(()=>{
// console.log("111");
// setCount(countDown);
// },[countDown])
return <>
<Button type="primary" disabled={isWait} onClick={() => {
task();
setIsWait(true);
setCount(countDown);
}} {...props}>{isWait ?count + "秒后" + waitText : normalText}</Button>
</>
}
第二天想起来,这段代码显然是有问题的,虽然它的效果看着挺正常的……它现在的效果是:count改变->useEffect内部执行清除定时器,然后又创建定时器,然后对count进行-1操作->useEffect执行,清除刚刚的定时器,又创建新的定时器……
所有它其实一直是创建定时器,-1,清除刚刚的定时器,创建新的定时器,-1,清除,创建……这样虽然基本上是1秒减1,但是其实完全不是我想要的效果。
后来想了一下,应该是开始等待isWait为true时,启动计时器,在计时器进行了countDown次操作后,清除计时器。同时在useEffect执行的那一刻,就形成了一个闭包,此时在计时器里面的count都是形成闭包时的数据,也就是计时器每次运行时,count还是一开始的count(但是因为是setCount(count => count-1)这样写的,外部的count是正常地进行着1秒减1的操作的。所以我加了一个局部变量,-1操作的次数都用它来记录,当达到倒计时要求的次数时,就可以终止倒计时了。可能有点冗余,但是应该是对的哈哈:
useEffect(()=>{
if(isWait === true){
clearInterval(timer.current);
let countTime = 0;
timer.current = setInterval(()=>{
setCount(count => count -1);
countTime ++;
if(countTime === countDown){
setIsWait(false);
clearInterval(timer.current);
}
}, 1000);
}
}, [isWait]);