【踩坑】react函数组件实现手机验证码倒计时

问题描述

点击按钮获取手机验证码,按钮置灰,出现60s倒计时,倒计时结束按钮恢复正常。

示例图
分析:这是一个很常见且并不复杂的功能,在vue和react类组件中我们通常会使用setInterval来实现,以下以vue2为例:

export default { 
    ……
    methods: {
        startCountdown() { 
            if (this.countdown === 0) { 
                this.countdown = 60; 
                this.timer = setInterval(() => { 
                    this.countdown--; 
                    if (this.countdown === 0) {
                        clearInterval(this.timer); 
                    } 
                }, 1000); 
            } 
        } 
    }, 
    ……
}; 

但是在react函数组件中,尝试用相同的方法,却实现不了此功能。这是因为回调函数中的状态值在每次渲染时并不会自动更新。以下是错误示例

import React, { useState, useEffect } from 'react';

const CountdownTimer = () => {
    const [count, setCount] = useState(60);
    useEffect(() => { 
        const interval = setInterval(() => {
            setCount(count - 1); 
        }, 1000); 
        // 清除定时器 
        return () => clearInterval(interval); 
    }, []); 
    return <div>Count: {count}</div>; 
};

export default CountdownTimer;

在这个例子中,setInterval 回调函数中的 count 值为初始值 60,并且不会随渲染更新。因此,count 会一直减到 59,然后保持不变。

解决方法

一、使用useRef保存最新的状态

import React, { useState, useEffect, useRef } from 'react';

const CountdownTimer = () => {
  const [count, setCount] = useState(60);
  const countRef = useRef(count);

  useEffect(() => {
    countRef.current = count;
  }, [count]);

  useEffect(() => {
    const interval = setInterval(() => {
      if (countRef.current > 0) {
        setCount(countRef.current - 1);
      } else {
        clearInterval(interval);
      }
    }, 1000);

    // 清除定时器
    return () => clearInterval(interval);
  }, []);

  return <div>Count: {count}</div>;
};

export default CountdownTimer;

二、使用setTimeout结合usEffect,代替setInterval(🌟推荐🌟)

import React, { useState, useEffect, useRef } from 'react'; 
import { Button } from 'antd'

const CountdownTimer = () => {
    const [codeBtnDisabled, setCodeBtnDisabled] = useState(false)
    const [countDown, setCountDown] = useState(30)
    let timerId: number| null = null
    useEffect( ()=> {
      if (codeBtnDisabled) {
        timerId = setTimeout(() => setCountDown(countDown - 1), 1000)
      }
      if (countDown <= 0){
        setCodeBtnDisabled(false)
        setCountDown(30)
      }
      return () => {
        if (timerId) clearTimeout(timerId)
      }
    },[countDown, codeBtnDisabled])
    
    return <Button disabled={ codeBtnDisabled }>
        { codeBtnDisabled ? `${count}s` : '获取手机验证码‘}
    </Button>;
};

export default CountdownTimer;
  • 8
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值