[React] Debouncing with useCallback Hook
本文介紹 React 當中 useCallback 這個 Hook 的使用概念,並且結合 Lodash 實作 Debouncing Search 的功能幫助理解 useCallback 的作用。
什麼是 useCallback Hook
useCallback
允許我們儲存跨元件執行的「函式」,它會把函式儲存在 React 內存記憶體,讓使用時的物件位址都是指向同一個地方,以避免無謂的重新渲染。
使用方式就是用 useCallback
包住我們要儲存的函式,它會回傳一個值(也就是你包裝的那個函式)。當我們包裝完成後,這個函式的 Functional Component 再進行重新評估執行時,函式就不會再被重新建立了。
1// 回傳一個 memoized 的 callback 2const memoizedCallback = useCallback(() => { 3 doSomething(a, b); 4}, [a, b]); 5
這時候使用像是
React.memo
時,就能夠知道傳入的函式是相同的物件。儘管 Props 傳入的是 Object,仍可以在父元件重新渲染時,不會重新分配記憶體位址,減少不必要的重新渲染
Dependency Array
在 useCallback
裡面也要放 Dependencies,要放的內容其實就跟 useEffect
一樣,就是把我們在函式中使用到的、關於元件的任何東西,像是 Props 或者 State 都放在裡面。另外,就如同 useEffect
的規則,Browser 內建功能如 LocalStorage 或是 React 的 setState 等等是不需要被放入的。
1// 這裡用到 setState 但是不用放入 dependency,因為 React 保證 setState 不會變化 2const toggleParagraphHandler = useCallback(() => { 3 setShowParagraph((prevShowParagraph) => !prevShowParagraph); 4}, []); 5
如果函式有使用到一些變數,因為有 Closure 關住原本的狀態,所以就算外部更改了變數,此時 useCallback
包住的函式的變數值,仍然會是傳入時的值。
如果希望根據變數改變,而重新生成函式的話,我們就得把那個變數加入 Dependency。例如:我們希望 allowToggle
更改時,就重新生成 toggleParagraphHandler
Function。
1const toggleParagraphHandler = useCallback(() => { 2 if (allowToggle) { 3 setShowParagraph((prevShowParagraph) => !prevShowParagraph); 4 } 5}, [allowToggle]); 6
範例練習:Lodash Debounce with React Hooks
useCallback
常見的其中一個使用情境,就是在實作 Throttle 或 Debounce 功能的時候。
搜尋時,我們透過 Lodash 的 _.debounce()
可以得到一個回傳的 Debounced 函式,並且函式使用 window.setTimeout
處理的 Timer 每次都不一樣,所以可以達到等待輸入的效果。換句話說,每一次回傳的 Debounced 函式都會是全新的,因此我們必須想辦法記憶這個函式,避免 React 元件重新渲染。
方法一:透過 useRef 避免重新渲染
我們可以透過 useRef
來記錄資料,因為透過 useRef
記錄的資料即使改變也不會造成重新渲染(若使用 useState
則會造成 Re-render)。
做法如下,使用 useRef
儲存資料,並使用 .current
屬性取得最新的 Debounced Function。
1const debouncedSearch = useRef( 2 debounce(async (params) => { 3 await doSearch(params); 4 }, 1000), 5 [], 6).current; 7
方法二:透過 useCallback 記憶函式
除了上述 useRef
的實作方式之外,使用 useCallback
也可以完成一樣的效果,同時能夠讓程式碼更加語意化、更好理解,個人比較推薦這個做法。
1const debouncedSearch = useCallback( 2 debounce(async (params) => { 3 await doSearch(params); 4 }, 1000), 5 [], 6); 7
回顧
看完這篇文章,我們到底有什麼收穫呢?藉由本文可以理解到…
- React useCallback Hook
- Lodash Debounce with React Hooks