iT邦幫忙

2022 iThome 鐵人賽

DAY 10
0
Modern Web

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

[DAY 10] 自己的Hook自己做!useCallbackEvent 真的有必要嗎?

  • 分享至 

  • xImage
  •  

回顧前一篇的困饒:「useCallback 中有從 props 傳來的 inner function 要如何正常運作?」

由於我們的 function 一直被重新建立的關係,在 useCallback 反而不如預期的運作,這時候我們可以搭配 useRef + useEffect 來達成。

DEMO-SITE 在這裡

甚麼是 useCallbackEvent?

亦或是 useEventCallback、useCallbackRef 類似的名詞組合可以透過 Google 搜尋都會查到類似的內容

簡單來說,這個 Hook 可以解決上述的情境,其實就是將傳入的 function 存放入 ref 並透過其性質來持續更新,簡易版本如下:

useEventRef

function useEventRef(callback) {
  const eventRef = useRef(callback)

  eventRef.current = callback

  return (...args) => eventRef.current?.(...args)
}

來嘗試加入到 useToggle 裡面:

useToggle

function useToggle(props = {}) {
  const { defaultState, onOn, onOff } = props

  if (defaultState !== undefined && typeof defaultState !== "boolean") {
    throw new Error("UseToggle: defaultState should be Boolean")
  }
  const [state, setState] = useState(defaultState || false)

  const onOnEventCallback = useEventRef(onOn)
  const onOffEventCallback = useEventRef(onOff)

  const toggleOn = useCallback(() => {
    setState(true)
    onOnEventCallback()
  }, [])

  const toggleOff = useCallback(() => {
    setState(false)
    onOffEventCallback()
  }, [])

  const toggle = useCallback(() => {
    const action = state ? toggleOff : toggleOn
    action()
  }, [state])

  return { isOn: state, toggle, toggleOn, toggleOff }
}

VOILA! 但這樣還不是最完美的,為了確保為「最新的」,可以再加入 useEffect,並可依照當下情境再加入 deps

useEffect(() => {
  eventRef.current = callback
}, deps)

若有 reference equality 的需求可以再加入 useCallback

return useCallback((...args) => eventRef.current?.(...args),[])

useEffect & useLayoutEffect 的差別

在探索這個 hook 時,發現有些是使用 useLayoutEffect

useEffect(() => {
  eventRef.current = callback
})

or

useLayoutEffect(() => {
  eventRef.current = callback
})

透過官方可以知道 useEffect 與 useLayoutEffect 本質上有相同的行為,但觸發時間的時間點稍微不同之外,且 useLayoutEffect 在 server-side 上是沒有任何作用的,因此也可以找到類似這樣的寫法:

function useSafeEffect() {
  return typeof window !== 'undefined' ? useLayoutEffect : useEffect
}

useSafeEffect(() => {
  eventRef.current = callback
})

結論

所以,這樣的useToggle 結構與 useEventRef 真的有必要嘛?

若今天只是從一個元件做邏輯上的分離,就不用這麼麻煩,但若是以共用的 library 來看,這類的情況就要考慮進去,雖然做法上有點取巧,可以感受到日後回來維護時腦筋又要再一次打結了 இдஇ


參考來源一一附上:

官方 Ref

相關討論串

MUI的useEventCallback

Charkra-ui的useCallbackRef

關於useLayoutEffect的相關討論串

一個類似的library但個人不是很愛他的方法


上一篇
[DAY 09] 自己的Hook自己做!useToggle 再進化,useToggle+!
下一篇
[DAY 11] 自己的Hook自己做!useDebounce 讓使用者慢~一~點~
系列文
React Hook 不求人,建立自己的 Hook Libary30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言