-
Notifications
You must be signed in to change notification settings - Fork 57
Description
Describe the issue
A clear and concise description of what the issue is.
In the current useOutsideClickEffect hook usage, you create a state (wrapperEl), connect the setter function to the container, and pass the state as an argument to the hook, as shown below:
function Example() {
const [wrapperEl, setWrapperEl] = useState<HTMLDivElement | null>(null);
useOutsideClickEffect(wrapperEl, () => {
console.log('Clicked outside!');
});
return <div ref={setWrapperEl}>Content</div>;
}I find this approach somewhat scattered and inconvenient to use because related codes are spread out. Therefore, I created a hook that returns a callback ref function, which you simply connect to elements, making usage simpler:
const useOutsideClick = (callback: () => void) => {
const elementSetRef = useRef<Set<HTMLElement>>(new Set());
useEffect(() => {
const handleDocumentClick = ({ target }: MouseEvent) => {
const elements = Array.from(elementSetRef.current);
const isOutsideClick = elements.every(
(element) => !element.contains(target as Node),
);
if (isOutsideClick && elements.length > 0) {
callback();
}
};
document.addEventListener("mousedown", handleDocumentClick);
return () => {
document.removeEventListener("mousedown", handleDocumentClick);
};
}, [callback]);
return (element: HTMLElement | null) => {
if (!element) {
return;
}
elementSetRef.current.add(element);
return () => {
elementSetRef.current.delete(element);
};
};
};
// Example of use
function App() {
const [isOpen, setIsOpen] = useState(true);
const addToSafeZone = useOutsideClick(() => setIsOpen(false));
return (
<div>
<div>{String(isOpen)}</div>
<div ref={addToSafeZone}>A</div>
<div ref={addToSafeZone}>B</div>
<div>content</div>
</div>
);
}I think this approach has the advantage of being simpler to use without requiring the user to create additional state manually.
Is there any perspective that I might be missing? If my approach is indeed better, I would like to improve the useOutsideClickEffect hook accordingly. I am curious about the maintainers’ opinions on this. :)