学了一个礼拜的React,刚好看到了公司里的小程序项目,里面有个登录获取验证码倒计时的按钮,就想用hooks实现下。
首先分析需求,组件内需要三个变量,分别存储文字,按钮状态和倒计时时间。
const [content, setContent] = useState('获取验证码')
const [btnDisabled, setBtnDisabled] = useState(false)
const [count, setCount] = useState(60)
设置一个button按钮给点击事件,按下后状态变为disabled,开始定时器每秒减一,当时间为0时,清除定时器,重置会原来的状态。
实现的逻辑并不复杂
首先我将时间的这部分抽离出来,写成自定义hook。一开始我在函数最外部定义来存储定时器,但后来发现如果同时调用两个后,定时器是同一个。
import { useState, useEffect, useRef } from "react";
// let timerId
export default function useCountDown(initCount = 60) {
const [count, setCount] = useState(() => initCount)
const timerId = useRef(null)
// 设置清除定时器,避免count还未为0时,组件已被Unmount
useEffect(() => {
return () => {
clearInterval(timerId.current)
}
}, [])
// 监听count的变化
useEffect(() => {
if (count === 0) {
clearInterval(timerId.current)
setCount(60)
}
}, [count])
// 定义定时器,每秒减一
function run () {
timerId.current = setInterval(() => {
setCount(pre => pre - 1)
}, 100)
}
return {count, run}
}
这里我还用到了memo,可以减少不必要的渲染,我还遇到了一个问题,我想使用useCallback存储最开始的数据。但是虽然log出的数据是改变了,但并没有在页面上重新渲染。这可能是React中回调触发渲染有关。还是得继续学习。
import { memo, useCallback } from "react";
import { useState, useEffect } from "react";
import useCountDown from "./useCountDown";
export default memo(function CountDownButton() {
const [content, setContent] = useState('获取验证码')
const [btnDisabled, setBtnDisabled] = useState(false)
const {count, run} = useCountDown()
useEffect(() => {
if (btnDisabled) {
setContent(`${count}秒后`)
}
if (count === 0) {
btnStatusReset()
}
}, [count])
// 按钮点击事件
function handleBtnClick () {
setBtnDisabled(true)
setContent(`${count}秒后`)
run()
}
// 重置按钮状态
// const btnStatusReset = useCallback(() => {
// setContent('获取验证码')
// setBtnDisabled(pre => pre)
// console.log(content, btnDisabled)
// }, [])
const btnStatusReset = () => {
setContent('获取验证码')
setBtnDisabled(false)
}
return (
<>
<input type="text" />
<button disabled={btnDisabled} onClick={handleBtnClick}>{content}</button>
</>
)
})
