use-dropdown.js 1.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354
  1. import { useCallback, useEffect, useRef, useState } from 'react'
  2. import { findDOMNode } from 'react-dom'
  3. export default function useDropdown(defaultOpen = false) {
  4. const [open, setOpen] = useState(defaultOpen)
  5. // store the dropdown node for use in the "click outside" event listener
  6. const ref = useRef(null)
  7. // react-bootstrap v0.x passes `component` instead of `node` to the ref callback
  8. const handleRef = useCallback(
  9. component => {
  10. if (component) {
  11. // eslint-disable-next-line react/no-find-dom-node
  12. ref.current = findDOMNode(component)
  13. }
  14. },
  15. [ref]
  16. )
  17. // prevent a click on the dropdown toggle propagating to the original handler
  18. const handleClick = useCallback(event => {
  19. event.stopPropagation()
  20. }, [])
  21. // handle dropdown toggle
  22. const handleToggle = useCallback(value => {
  23. setOpen(value)
  24. }, [])
  25. // close the dropdown on click outside the dropdown
  26. const handleDocumentClick = useCallback(
  27. event => {
  28. if (ref.current && !ref.current.contains(event.target)) {
  29. setOpen(false)
  30. }
  31. },
  32. [ref]
  33. )
  34. // add/remove listener for click anywhere in document
  35. useEffect(() => {
  36. if (open) {
  37. document.addEventListener('mousedown', handleDocumentClick)
  38. }
  39. return () => {
  40. document.removeEventListener('mousedown', handleDocumentClick)
  41. }
  42. }, [open, handleDocumentClick])
  43. // return props for the Dropdown component
  44. return { ref: handleRef, onClick: handleClick, onToggle: handleToggle, open }
  45. }