useOutsideClick.ts 1.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142
  1. import { useEffect, useRef } from "react";
  2. export const useOutsideClickHook = (handler: (event: Event) => void) => {
  3. const ref = useRef(null);
  4. useEffect(
  5. () => {
  6. const listener = (event: Event) => {
  7. const current = ref.current as HTMLElement | null;
  8. // Do nothing if clicking ref's element or descendent elements
  9. if (
  10. !current ||
  11. current.contains(event.target as Node) ||
  12. [...document.querySelectorAll("[data-prevent-outside-click]")].some(
  13. (el) => el.contains(event.target as Node),
  14. )
  15. ) {
  16. return;
  17. }
  18. handler(event);
  19. };
  20. document.addEventListener("pointerdown", listener);
  21. document.addEventListener("touchstart", listener);
  22. return () => {
  23. document.removeEventListener("pointerdown", listener);
  24. document.removeEventListener("touchstart", listener);
  25. };
  26. },
  27. // Add ref and handler to effect dependencies
  28. // It's worth noting that because passed in handler is a new ...
  29. // ... function on every render that will cause this effect ...
  30. // ... callback/cleanup to run every render. It's not a big deal ...
  31. // ... but to optimize you can wrap handler in useCallback before ...
  32. // ... passing it into this hook.
  33. [ref, handler],
  34. );
  35. return ref;
  36. };