import React, { useRef, useEffect } from "react";
/**
* Hook that alerts clicks outside of the passed ref
*/
function useOutsideAlerter(ref) {
useEffect(() => {
/**
* Alert if clicked on outside of element
*/
function handleClickOutside(event) {
if (ref.current && !ref.current.contains(event.target)) {
alert("You clicked outside of me!");
}
}
// Bind the event listener
document.addEventListener("mousedown", handleClickOutside);
return () => {
// Unbind the event listener on clean up
document.removeEventListener("mousedown", handleClickOutside);
};
}, [ref]);
}
/**
* Component that alerts if you click outside of it
*/
export default function OutsideAlerter(props) {
const wrapperRef = useRef(null);
useOutsideAlerter(wrapperRef);
return <div ref={wrapperRef}>{props.children}</div>;
}
import { useEffect, MutableRefObject } from 'react'
export const useOutsideClick = <T extends Array<MutableRefObject<any>>>(
ref: T,
callback: () => void
): void => {
useEffect(() => {
const handler = (event: MouseEvent): void => {
// Check if the mouse click was within the element's ref.
if (!ref || ref.length === 0) return
const node = ref.find((x) => x?.current?.contains(event?.target as Node))
if (!node) {
callback()
}
}
window.addEventListener('mousedown', handler)
return (): void => {
window.removeEventListener('mousedown', handler)
}
}, [ref, callback])
}
// Usage (it should be in the component*)
const firstRef = useRef(null)
const secondRef = useRef(null)
const handleClick = () => { console.log('Clicked outside ref') }
useOutsideClick([firstRef, secondRef], handleClick)
import { useEffect } from 'react';
// Hook
function useOnClickOutside(ref, buttonRef, handler) {
useEffect(
() => {
const listener = event => {
// Do nothing if clicking ref's element or descendent elements
if (!ref.current || ref.current.contains(event.target) || buttonRef.current.contains(event.target)) {
return;
}
handler(event);
};
document.addEventListener('mousedown', listener);
document.addEventListener('touchstart', listener);
return () => {
document.removeEventListener('mousedown', 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]
);
}
export default useOnClickOutside;
//component/Header.tsx
const header = () => {
const ref = useRef(null);
const buttonRef = useRef(null);
console.log(buttonRef);
useOnClickOutside(ref, buttonRef, () => setIsOpen(false));