iT邦幫忙

2022 iThome 鐵人賽

DAY 15
0
Modern Web

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

[DAY 15] 自己的Hook自己做!useClickOutside 來把 Dialog 關起乃!

  • 分享至 

  • xImage
  •  

再續前一篇研究了一輪,這篇就來實作:

DEMO 在這裡~~

需求及描述

透過 Hook 可以達成:

  • 點擊非指定元素時(即元素之外),能夠執行特定動作

以 Dialog 情境來說,點擊非 Dialog 本身要能夠進一步關閉。

開始!

首先,建立一個類似 Dialog 的內容,並且可以開開關關:

function Example() {
  const [open, setOpen] = useState(false)

  const handleToggle = () => {
    setOpen((prev) => !prev)
  }

  return (
    <>
      <Button onClick={handleToggle}>Open</Button>
      <PortalDialog open={open} onClose={handleToggle} />
    </>
  )
}

<PortalDialog/> 則是利用 Chakra-UI <Portal/> 來處理 Dialog 渲染到document.body 中的最後一個子元素,<PortalDialog/> 則是長這樣子:

function PortalDialog(props) {
  if (!props.open) return null

  return (
    <Portal>
      <Dialog {...props} />
    </Portal>
  )
}

主角 useClickOutside

function useClickOutside(cb) {
  const ref = useRef()

  useEffect(() => {
    const handleClick = (event) => {
      const target = event.target

      const isInside = ref.current.contains(target)


      if (!isInside) {
        //do the job
        cb()
      }
    }

    document.addEventListener("click", handleClick)

    return () => {
      document.removeEventListener("click", handleClick) 
    }
  }, [])

  return ref
}

我們搭配 useEffect + useRef 的組合,這邊的 ref 是要來存取 DOM element 的,會在這個元素上額外「手動」添加 click event;handleClick 是主要的核心:

const isInside = ref.current.contains(target)

而功能就是當點擊的目標並不存在於 element 裡面的話,就會執行一開始所傳入的 callback。

定義好「外面」

<Dialog/> 本身是長這樣:

function Dialog({ onClose }) {
  const ref = useClickOutside(onClose) //把要執行的cb傳入 hook

  return (
    <Overlay>  // 將內容呈現最上方
      <Dialog ref={ref}> // 點擊的範圍,這邊的範圍之外,都是「外面」
        <Text>{"Hello (ノ>ω<)ノ"}</Text>
        <Button colorScheme="red" onClick={onClose}>
          Close
        </Button>
      </Dialog>
    </Overlay>
  )
}

Overlay 與 Dialog 只是一般的 div element,這邊把結構語意化比較好理解。嘗試重現的話,要再留意是否需要fowardRef。

如圖,期望的點擊範圍就是紅線虛線所包起來的白色區域(Dialog)

我們把 overlay 再加上一點顏色,來讓整個 Dialog 更明顯,這樣功能大致上就完成了!

結語

useClickOutside 並沒有特定侷限要用在 dialog 上,也可以用於其他需要搭配點擊外面來觸發額外動作的情境;進一步,其他的事件像是觸發頻率更高的 mouseover,也可以用一樣的概念製作。


上一篇
[DAY 14] 自己的Hook自己做!網頁常看到的 Dialog 其實不會自己關?!
下一篇
[DAY 16] 自己的Hook自己做!等等,請先登入!
系列文
React Hook 不求人,建立自己的 Hook Libary30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言