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

前言

最近在做一个 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 到,还是不能太自信啊~

相关推荐
我即将远走丶或许也能高飞35 分钟前
vuex 和 pinia 的学习使用
开发语言·前端·javascript
钟离墨笺1 小时前
Go语言--2go基础-->基本数据类型
开发语言·前端·后端·golang
爱吃泡芙的小白白1 小时前
Vue 3 核心原理与实战:从响应式到企业级应用
前端·javascript·vue.js
卓怡学长2 小时前
m115乐购游戏商城系统
java·前端·数据库·spring boot·spring·游戏
码上成长2 小时前
JavaScript 数组合并性能优化:扩展运算符 vs concat vs 循环 push
开发语言·javascript·ecmascript
老陈聊架构2 小时前
『AI辅助Skill』掌握三大AI设计Skill:前端独立完成产品设计全流程
前端·人工智能·claude·skill
油丶酸萝卜别吃2 小时前
Mapbox GL JS 表达式 (expression) 条件样式设置 完全指南
开发语言·javascript·ecmascript
Ulyanov3 小时前
从桌面到云端:构建Web三维战场指挥系统
开发语言·前端·python·tkinter·pyvista·gui开发
cypking3 小时前
二、前端Java后端对比指南
java·开发语言·前端
摘星编程3 小时前
用React Native开发OpenHarmony应用:timing定时动画参数
javascript·react native·react.js