iT邦幫忙

1

React Custom hook 踩坑日記 - useDebounce

  • 分享至 

  • xImage
  •  

今天要分享的是我們在優化前端時常會做的處理,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的時間,而且有團隊維護應該還是比較省事的做法,一樣推薦給各位,希望還是有幫助到大家!


圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言