← 所有文章

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

本文目录

前言

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

 

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

解决方案

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

Talk is cheap, show me the code!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
  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 到,还是不能太自信啊~

留言

欢迎交流想法,也欢迎指出文中的问题。

在文章里找一找

搜索标题和正文,直接跳到相关章节。