今天要分享的是我們在優化前端時常會做的處理,Debounce & Throttle,那關於這兩個名詞的解釋,網路上有很多專門在講解的文章,也可以參考連結。
所以,以實現這樣的功能來說我們會需要一個setTimeout來當作阻擋的中繼器,我們可以用custom hook的概念做組建的拆分,用來實踐兩種不同情境的功能。
首先,我們先試者製作useTimeout的custom hook:
import { useCallback, useEffect, useRef } from "react";
const useTimout = (callback, delay) => {
const callbackRef = useRef(callback)
const timeoutRef = useRef()
// 透過 ref change 條件觸發更新事件
useEffect(() => {
callbackRef.current = callback
}, [callback])
// 透過 useCallback 減少重複 setTimeout
const set = useCallback(() => {
timeoutRef.current = setTimeout(() => callbackRef.current(), delay)
}, [delay])
const clear = useCallback(() => {
timeoutRef.current && clearTimeout(timeoutRef.current)
}, [])
// 這裡是主要的作用 hook 主要做值更改的部分,在更動完後清除timeout
useEffect(() => {
set()
return clear
}, [delay, set, clear])
const reset = useCallback(() => {
clear()
set()
}, [clear, set])
// 如果不用 clear, reset function 也可以不定義
return { clear, reset }
}
export default useTimout;
這個hook就是一個計時器功能的作用,有clear & reset兩個function可以做調整,使用範例:
// ...省略import useTimeout的做法
const exampleComponent = () => {
const [count, setCount] = useState(0)
const {clear, reset} = useTimeout(() => setCount(10), 1000)
return (
<div>
<h1>useTimout</h1>
<p>{count}</p>
<Button p={`1rem`} onClick={() => setCount(prev => prev + 1)}>++</Button>
<Button p={`1rem`} onClick={clear}>clear</Button>
<Button p={`1rem`} onClick={reset}>reset</Button>
</div>
)
}
跑起來後會在一秒之後自動執行() => setCount(10)
的function, 也可以當倒數計時器使用。
接著我們照這debounce的執行邏輯去製作useDebounce的custom hook:
import { useEffect } from "react"
import useTimout from "./useTimeout"
// 等待動作停止後,觸發其他function,適合用於query api
const useDebounce = (callback, delay, dependencies) => {
// 這邊直接將動作和delay時間帶入useTimeout
const { reset, clear } = useTimout(callback, delay)
// dependencies是整個array, 當然你可以只帶入裡面的值
// 這裡是透過reset重新計時
useEffect(reset, [...dependencies, reset])
// 這裡不希望一開始就觸發callback,所以加了一個clear
useEffect(clear, [])
}
export default useDebounce;
使用範例如下:
// ...省略import hooks
const exampleComponent = () => {
const [count, setCount] = useState(0)
// 這裡的alert只是示範,實務上可以改成其他動作的function
useDebounce(() => alert(count), 1000, [count])
return (
<div>
<h1>useDebounce</h1>
<p>{count}</p>
<Button p={`1rem`} onClick={() => setCount(prev => prev + 1)}>++</Button>
</div>
)
}
設想一下這類的功能如果放在原本searchbar input的功能欄位,可以有效降低不必要的query,所以在實際應用上是很常見的功能。
那麼,以上的範例就是這一篇的分享,Throttle的部分就讓你們自己練習吧!
基本上邏輯是差不多的,這系列的更新可能就到這篇吧,因為有太多第三方的套件已經整合好這類custom hook的function,這也是為什麼那麼久沒更新了,最近轉而使用typescript在專案上,像usehook或是mantine這類的套件,能大幅降低自己寫custom hook的時間,而且有團隊維護應該還是比較省事的做法,一樣推薦給各位,希望還是有幫助到大家!