123456789101112131415161718192021222324252627282930313233343536373839404142 |
- import { useEffect, useRef } from "react";
- export const useOutsideClickHook = (handler: (event: Event) => void) => {
- const ref = useRef(null);
- useEffect(
- () => {
- const listener = (event: Event) => {
- const current = ref.current as HTMLElement | null;
- // Do nothing if clicking ref's element or descendent elements
- if (
- !current ||
- current.contains(event.target as Node) ||
- [...document.querySelectorAll("[data-prevent-outside-click]")].some(
- (el) => el.contains(event.target as Node),
- )
- ) {
- return;
- }
- handler(event);
- };
- document.addEventListener("pointerdown", listener);
- document.addEventListener("touchstart", listener);
- return () => {
- document.removeEventListener("pointerdown", listener);
- document.removeEventListener("touchstart", listener);
- };
- },
- // Add ref and handler to effect dependencies
- // It's worth noting that because passed in handler is a new ...
- // ... function on every render that will cause this effect ...
- // ... callback/cleanup to run every render. It's not a big deal ...
- // ... but to optimize you can wrap handler in useCallback before ...
- // ... passing it into this hook.
- [ref, handler],
- );
- return ref;
- };
|