debounce 都提到了,怎麼能不提 throltte 呢?

在玩 Pokemon Go 打對戰的時候,有時候想著按快一點把對方打趴,殊不知再怎麼快,你的神奇寶貝似乎也快不上你。
其他遊戲也有類似情境,例如點擊時角色會進行攻擊,按快一點角色還是會乖乖跑完第一下的動畫,等動畫完成之後,等你按「真正」的第二下才會繼續動作 (儘管前面按了三千多下),或像是連續點擊的技能,每點一下會有1秒的CD光圈跑完才能繼續按第二下,CD時間點擊了也不會有任何反應。
但有時候戰況激烈時,為了拚那 0.1 秒的死活真的是會不停瘋狂的按,為了這一刻,我們來製作一個點擊練習器吧!
刻意練習 throlttle (X)
製作無意義的功能 (O)
首先,我們需要一個視覺化的量表,Chakra-UI 有 <CircularProgress /> ,將它小小改造一下變成可點擊的按鈕。

(好的,看起來很欠按)
期望是:
首先,先把點擊功能製作出來,大概的樣子:
function Example() {
const [value, setValue] = useState(0)
const handleClick = () => {
setValue((prev) => {
const nextValue = prev + 1
return nextValue >= 100 ? 100 : nextValue
})
}
return (
<Stack align="center">
<CircularProgress value={value} onClick={handleClick} />
</Stack>
)
}
function useThrottle(cb, time = 500) {
const isEnableRef = useRef(true)
return (...args) => {
if (!isEnableRef.current) {
return
}
isEnableRef.current = false
cb(...args)
setTimeout(() => {
isEnableRef.current = true
}, time)
}
}
與 debounce 不同在於,throltte 是立即觸發,而等到 isEnableRef 為 true 時,才允許下一次的執行,而 debounce 則是只要一直觸發持續延遲執行。
一樣是使用了 useRef 與 setTimeout 搭配組合,當觸發執行回傳的 function 時,isEnableRef 會變為 true,一直到 setTimeout 跑完給定的秒數時,才會變回 false,這時 function 就可以再執行一次 (準確來說,function 是可以一直被執行,但因 isEnableRef 為 false 的關係則 early return,當變回 true 時,就能順利執行後續的程式)。
| Param | Type | Description |
|---|---|---|
cb |
function | callback,當 setTimeout 的 time 時間到時會更改 isEnableRef 的狀態 |
time |
number | 單位ms, 定義 setTimeout 要 time 多久,default 為 500ms |
| Return | Type | Description |
|---|---|---|
() => {} |
function | 執行的event,其中傳入的參數會進一步傳入一開始的cb |
我們把原本的 setValue 傳入 useThrottle,並將回傳的 throttledEvent 替換原本 setValue 所放置的位置,也另外加上 <Timer/> 跟重置按鈕。
function Example() {
const [value, setValue] = useState(0)
const throttledEvent = useThrottle(setValue, 450)
const handleClick = () => {
throttledEvent((prev) => {
const nextValue = prev + 1
return nextValue >= 100 ? 100 : nextValue
})
}
return (
<Stack align="center">
<Timer value={value} />
<CircularProgress value={value} onClick={handleClick} />
<Button onClick={() => setValue(0)} disabled={value !== 100}>
RESET
</Button>
</Stack>
)
}
然後就完成啦!

恩,手指好酸