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>
)
}
然後就完成啦!
恩,手指好酸