学了一个礼拜的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> </> ) })