Skip to content

Instantly share code, notes, and snippets.

@anthowen
Last active September 20, 2022 09:49
Show Gist options
  • Save anthowen/e2a50f506bd6fb29f16bc898286ecac5 to your computer and use it in GitHub Desktop.
Save anthowen/e2a50f506bd6fb29f16bc898286ecac5 to your computer and use it in GitHub Desktop.
useOutsideClick (TypeScript) - a custom react hook that handles outside click event of certain area. It also ignores click event on browser's scrollbar.
import { useEffect, RefObject } from 'react';
/**
* Hook that handles outside click event of the passed refs
*
* @param refs array of refs
* @param handler a handler function to be called when clicked outside
*/
export default function useOutsideClick(
refs: Array<RefObject<HTMLElement> | undefined>,
handler?: () => void,
) {
useEffect(() => {
function handleClickOutside(event: any) {
if (!handler) return;
// Clicked browser's scrollbar
if (
event.target === document.getElementsByTagName('html')[0] &&
event.clientX >= document.documentElement.offsetWidth
)
return;
let containedToAnyRefs = false;
for (const rf of refs) {
if (rf && rf.current && rf.current.contains(event.target)) {
containedToAnyRefs = true;
break;
}
}
// Not contained to any given refs
if (!containedToAnyRefs) {
handler();
}
}
// Bind the event listener
document.addEventListener('mousedown', handleClickOutside);
return () => {
// Unbind the event listener on clean up
document.removeEventListener('mousedown', handleClickOutside);
};
}, [refs, handler]);
}
@Johannes5
Copy link

Johannes5 commented Jun 30, 2022

Different onOutsideClick hooks I've tried so far caused the problem that too many clicks were registered and inside clicks as well.
Because I have multiple instances of the component that should listen to outside clicks.

So I'm happy I found your hook. Now instead of using the hook inside of the component that wants to know about outside clicks, I use it in the parent.

But I wasn't sure how to assign the refs.

I tried this:


 const refs = React.useRef<MutableRefObject<HTMLElement | null>>(null);
    refs.current = [];
    console.log(refs);

  const addToRefs = (el: any) => {
        if (el && !refs.current.includes(el)) {
            refs.current.push(el);
        }
    };

    useOnClickOutside([...refs], handleClickOutside)


and this is how the refs are assigned:
image

The type definition inside your hook I changed to:
image

This is just me experimenting. I of course tried it with RefObject as well.
But I don't know what I'm doing, really. Refs are very confusing when it comes to Typescript.

My problem is that I get:
image
and
image

Maybe you know what to do?

@Johannes5
Copy link

@anthowen I still have this issue, so I'd be very happy if you had an idea

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment