다양한 컴포넌트를 만들면서 외부 영역 클릭시 닫히는 기능을 자주 쓰게 되는 것 같다.. ( modal, dropdownbox 등등.. )
사용 한 jsx 코드 예시
export const ListMenu = (props) => {
const horizontalMenuRef = useRef();
const [clickFlag, setClickFlag] = useState(false);
// 드랍다운 메뉴 밖 영역 클릭시 닫히게
useEffect(() => {
const handleOutsideClose = (e) => {
if (clickFlag && !horizontalMenuRef.current.contains(e.target)) setClickFlag(false);
};
document.addEventListener("click", handleOutsideClose);
return () => document.removeEventListener("click", handleOutsideClose);
}, [clickFlag]);
return (
<>
<DropdwonDiv ref={horizontalMenuRef}>
<MenuDiv onClick={ setClickFlag((prevValue) => !prevValue);} >
...
</MenuDiv>
</DropdwonDiv>
</>
);
};
위처럼 만들어서 사용하다가.. 이미지 처럼 필요할 때마다 컴포넌트 별로 중복된 코드를 계속 쓰게 되길래..
hooks 폴더를 만들고 재사용 가능한 js 파일을 만들어서 사용했다
useOutsideClose.js
import { useEffect } from "react";
const useOutsideClose = (clickFlag, refs, handler) => {
useEffect(() => {
const handleOutsideClose = (e) => {
if (clickFlag && !refs.current.contains(e.target)) handler(e);
};
document.addEventListener("click", handleOutsideClose);
return () => document.removeEventListener("click", handleOutsideClose);
}, [refs, handler]);
};
export default useOutsideClose;
jsx 내부에서는 아래와 같이 사용 하면된다.
// useOutsideClose 불러오기
import useOutsideClose from "../../hooks/handleOutsideClose";
...
// 기존 useEffect 작성을 useOutsideClose로 대체 ( 기존에 사용하던 state 를 넘기기 click state, ref )
useOutsideClose(clickFlag, horizontalMenuRef, () => {
setClickFlag(false);
});
useOutsideClose.js 내용에서 clickFlag 도 넘기지 않고 처리 하고 싶다면,
처리 함수 부분에 let 변수 하나를 두고 제어 해도 좋을 것 같다.