學了一個禮拜的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>
</>
)
})
