发送验证码后的节流倒计时丨刷新 & 重新进入页面,还原倒计时状态

前言

  最近在做一个 H5 工具,需要手机号 + 验证码登录,很自然地,点击发送验证码后需要等待一段时间才能重新发送,用于请求节流,避免用户疯狂点击:

  不过这里其实有个隐藏需求------如果仍然在冷却时间内,那么用户无论是刷新或是关闭页面,再次打开登录弹窗,需要直接展示正确的倒计时状态

解决方案

使用经典的 localStorage

  1. 发送验证码时,将发送时间 (lastSendingTime) 存入 localStorage,并开启 60 秒倒计时。
  2. 倒计时结束后,清除 localStorage 中的 lastSendingTime
  3. 重新进入页面时,若 localStorage 中存有 lastSendingTime,则说明仍处于冷却时间内,那么计算出剩余的倒计时 N,并开启 N 秒倒计时。

Talk is cheap, show me the code!

js 复制代码
  const [countdown, setCountdown] = useState(60) // 倒计时
  const [canSendCode, setCanSendCode] = useState(true) // 控制按钮文案的状态
  const [timer, setTimer] = useState() // 定时器 ID

  async function sendVerificationCode() {
    try {
      // network request...
      Toast.show({ content: '验证码发送成功' })
      startCountdown()
      setCanSendCode(false)
    } catch (error) {
      setCountdown(0)
      setCanSendCode(true)
    }
  }

  function startCountdown() {
    const nowTime = new Date().getTime()
    const lastSendingTime = localStorage.getItem('lastSendingTime')
    if (lastSendingTime) {
      // 若 localStorage 中存有 lastSendingTime,则说明仍处于冷却时间内,计算出剩余的 countdown
      const restCountdown = 60 - parseInt(((nowTime - lastSendingTime) / 1000), 10)
      setCountdown(restCountdown <= 0 ? 0 : restCountdown)
    } else {
      // 否则说明冷却时间已结束,则 countdown 为 60s,并将发送时间存入 localStorage
      setCountdown(60)
      localStorage.setItem('lastSendingTime', nowTime)
    }

    setTimer(
      setInterval(() => {
        setCountdown(old => old - 1)
      }, 1000),
    )
  }

  // 重新进入页面时,若 localStorage 中存有上次的发送时间,则说明还处于冷却时间内,则调用函数计算剩余倒计时;
  // 否则什么也不做
  useEffect(() => {
    const lastSendingTime = localStorage.getItem('lastSendingTime') 
    if (lastSendingTime) {
      setCanSendCode(false)
      startCountdown()
    }

    return () => {
      clearInterval(timer)
    }
  }, [])

  
  // 监听倒计时,倒计时结束时:
  // * 清空 localStorage 中存储的上次发送时间
  // * 清除定时器
  // * 重置倒计时
  useEffect(() => {
    if (countdown <= 0) {
      setCanSendCode(true)
      localStorage.removeItem('lastSendingTime')
      clearInterval(timer)
      setCountdown(60)
    }
  }, [countdown])

return (
  {canSendCode ? (
    <span onClick={sendVerificationCode}>
      获取验证码
    </span>
  ) : (
    <span>
      获取验证码({`${countdown}`})
    </span>
  )}
)

最终效果

总结

  一开始感觉这是个很简单的小需求,可能 20min 就写完了,但实际花了两个多小时才把逻辑全部 cover 到,还是不能太自信啊~

相关推荐
_阿南_6 小时前
项目中新增给AI制定的代码规范
前端·程序员
名字还没想好☜7 小时前
React 实现暗黑模式切换:localStorage 持久化、SSR 首屏闪烁与跟随系统主题
前端·javascript·react.js·ecmascript·react·next.js
自动化监测Learner7 小时前
主流 Web 端地图引擎对比:选型指南与优劣分析
javascript
console.log('npc')8 小时前
Git 冲突与 AI 协助指南
前端·人工智能·git·大模型
爱学堂IT分享9 小时前
Cesium可视化系统实战课程-Cesium教程学习
前端
糖墨夕9 小时前
理解大语言模型:Agent 的“大脑”
前端·agent
Rain的Java大神之路9 小时前
JavaWeb开发如何解决跨域问题
java·前端·后端·nginx·web安全·面试·运维开发
cpolar技术支持10 小时前
浏览器也能跑本地 AI:用 Transformers.js + WebGPU 做一个最小推理 Demo,cpolar 给同事远程体验
前端·ai·cpolar·webgpu·transformers.js
计算机魔术师11 小时前
OpenAI 发布 GPT-6 Astra:多项基准刷新纪录, cybersecurity 能力达 Critical 阈值
前端
xy345311 小时前
Axure9.0中继器遮罩实现方法
前端·ui·html·axure·原型·产品设计