之前很常忘記這兩個,常常記錯,更早之前是看到這兩個單字就直接跳過,因為感覺很艱深,一直到去年與同學一起做履歷作品的時候,因為面臨到一些技術問題,所以不得不開始了解 debounce 和 throttle。
所以對於學習到一個階段的同學,別害怕,終究會遇到的,因為已經開始擔任前端工程師的我,在未滿第三個月的現在也遇到了 T.T
Debounce
debounce 非常適合用在一些 autocomplete 的東西上,eg. search bar。
例如說當我們在 search bar input 輸入 debounce
時,如果沒有使用 debounce 概念的話會變成是當鍵盤輸入
d => 打 api 搜尋
e => 第二次打 api 搜尋
b => 第三次 ...
o => 第四次
..
..
如果加入了 debounce 概念之後,我們可以變成這樣
<假設delay設置 300 毫秒>
d => 當輸入 d 時,300毫秒內如果沒有再輸入的話就會打第一次 api
(如果300毫秒內輸入...)
e => 輸入 e 時,重新計時,300毫秒內如果沒有再輸入的話就會打第二次 api
(如果300毫秒內輸入...)
b => 輸入 b 時,300毫秒內如果沒有再輸入的話就會打第三次 api
..
..
接下來我們可以看正常的 input 和 加入 debounce 概念的 input 在 react 該怎麼實作:
這是兩個 input dom,第一個是 default,第二個待會會加入 debounce 。
//...
return (
<>
<div>
<input value={input} onChange={onInputChange} />
</div>
<div>
<input value={inputDebounce} onChange={onInputDebounceChange} />
</div>
</>
);
接下來看看 onInputChange 和 onInputDebounceChange 該怎麼實作:
input 的 input 綁定的 click 事件:
const onInputChange = (e) => {
setInput(e.target.value);
fetchData(e.target.value)
console.log('fire onInputChange');
};
inputDebounce 綁定的 click 事件以及需要用到的function:
const debounce = (fn, delay = 300) =>{
let timer
return (...args)=>{
console.log(timer)
if(timer){
clearTimeout(timer)
}
timer = setTimeout(()=>{
timer = null
fn(...args)
},delay)
}
}
const debounceHandler = useCallback(debounce(fetchData,3000),[])
const onInputDebounceChange = (e) => {
setInputDebounce(e.target.value)
debounceHandler(e.target.value)
}
當 user click 之後觸發了 onInputDebounceChange => setInputDebounce 更改 inputDebounce 的狀態 => debounceHandler 帶著 e.target.value
執行了 debounce function,第一個參數是 setTimeout ,也就是時間到了之後要做什麼動作(fetchData),以及第二個參數 300 (如果沒有帶第二個參數就會是預設的 1000 毫秒)。
核心是在 debounce 那個 function,debounce 會接收剛剛介紹的那兩個 function,然後會先宣告一個 timer,這是因為執行完這個 function 之後會需要用到這個 timer (因為要重新計時),最後 debounce 會回傳一個 function:
這個 function 會先清掉上一次的 timer,並且開始重新計時 delay (300) 幾秒後執行 fn (fetchData)。
是不是有點類似 closure 概念,沒錯!就是 closure,之前也有介紹過 closure,可以參考之前我寫的 JavaScript 程式執行原理:Closure。
完整的程式碼範例我會放在這裡,有需要的話可以參考:
https://stackblitz.com/edit/react-dqq5ht?file=src/App.js