iT邦幫忙

2022 iThome 鐵人賽

DAY 11
0
Modern Web

React Hook 不求人,建立自己的 Hook Libary系列 第 11

[DAY 11] 自己的Hook自己做!useDebounce 讓使用者慢~一~點~

  • 分享至 

  • xImage
  •  

image alt

前言

俗話說的好:Never trust user input,開發時設想好的流程,清楚的畫面,適當的輔助文字,使用者仍然還是會有「意想不到」的操作。

今天來面對其中一種使用者,工作速度很快,打字一分鐘三五百行,並對你建立的 AutoComplete 搜尋功能愛不絕口,只要簡單輸入幾個關鍵字就能把資料庫的資料列出來,但使用者疾風般打字速度真的是...

後端:「饒了我可以嗎 :)」

功能與情境

AutoComplete 指的是一種進階的 input,可以在使用者輸入文字時提供建議(或固定)的選項,不論是hard code或是透過API抓資料呈現,都是很好的搭配良器。

假設今天是串API取得的資料,可以透過輸入的 input 當作query,並將回傳的資料呈現出來供使用者選擇,但我們不想要每顆按鍵當要做一次request,因此我們加入 debounce 並搭配 react-hook 實做出來。

你可能比較想先看DEMO,在這裡

AutoComplete

Chakra-ui 沒有內建好的 AutoComplete,DEMO是將既有的元件簡單拼起來 (〒︿〒)

簡單拼起來後,AutoComplete 能接受這些 prop

Prop Type Description
data array 呈現列表的資料,當為長度為0時會呈現no option的字樣
inputValue any 控制 input 的 value
onInputChange function 當input改變時執行的callback
value any 給予 AutoComplete 本身的 value,當 value 有出現在呈現的列表時會額外highlight
onChange function 當點擊 item 時會執行的 callback
isLoading boolean 當 true 時,會進入 loading 的狀態並呈現讀取條

開始!

目前的行為是每個 key-storke 都會進行一次 request:

function Example() {
  const [choose, setChoose] = useState("")
  const [input, setInput] = useState("")
  const users = getUsers({ name: input, limit: 5 })

  return (
    <AutoComplete
      data={users}
      inputValue={input}
      onInputChange={setInput}
      value={choose}
      onChange={setChoose}
    />
  )
}
  • getUsers 模仿API抓取資料,接受兩種 query:name (搜尋名字) & limit (回傳筆數)
  • input 是給輸入使用的 state
  • choose 是保存選取的內容

實作 debounce

  • useState - 保存 setTimeout 本身是否正在執行/已執行的狀態
  • useEffect - 處理 setTimeout 流程,並透過傳入 deps 當作 trigger,clean-up 則是component un-mount 時 pending 的 setTimeout 也能一起被取消
  • useRef - 處理是否為第一次 mount,來略過第一次的執行

整體如下:

function useDebounce(cb, delay = 250, deps) {
  const initRef = useRef(true)
  const [isPending, setIsPending] = useState(false)

  useEffect(() => {
    if (initRef.current) {
      initRef.current = false
      return
    }

    setIsPending(true)
    const timeout = setTimeout(() => {
      cb()
      setIsPending(false)
    }, delay)

    return () => clearTimeout(timeout)
  }, deps)

  return isPending
}

Params

Param Type Description
cb function callback,當 setTimeout 的 delay 時間到時會觸發的動作
delay number 單位ms, 定義 setTimeout 要 delay 多久,default 為 250ms
deps array useEffect 的 deps,來決定要不要執行

Return

Return Type Description
isPending boolean 呈現 setTimeout是否執行的狀態

加進去

function Example() {
  const [choose, setChoose] = useState("")
  const [input, setInput] = useState("")
  
  const [delayInput, setDelayInput] = useState("")
  const isLoading = useDebounce(() => setDelayInput(input), 2000, [input])
  
  const users = getUsers({ name: delayInput, limit: 5 })

  return (
    <AutoComplete
      data={users}
      inputValue={input}
      onInputChange={setInput}
      value={choose}
      onChange={setChoose}
    />
  )
}

我們另外建立 delayInput , 並把 setDelayInput 放入到 useDebounce,設定 2000ms,並在 deps 加入 input 當作觸發。

這樣一來,疾風打字使用者打在快也不怕了!

結語

設計上較依賴其他的 state 來決定是否要觸發,假如不放入 deps,每次 render 就會執行一次。

功能延伸上可以再搭配快取或是 SWR 來記住曾經打過的 Request,面對重複的搜尋就不用再讓使用者等待過久。

參考連結

MUI AutoComplete

SWR


上一篇
[DAY 10] 自己的Hook自己做!useCallbackEvent 真的有必要嗎?
下一篇
[DAY 12] 自己的Hook自己做!能取消動作的 useEventControl!
系列文
React Hook 不求人,建立自己的 Hook Libary30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言