윈도우 디자인을 모토로 하기 때문에 모달을 자주 활용할 것 같아 모달 외부 클릭 시 자동으로 창을 닫히게 하는 기능을 훅으로 만들어 관리하고자 한다.
useEffect의 dependency를 outsideRef로 지정해주었다. outsideRef는 모달의 외부 영역의 DOM을 다룬다. 모달 외부 영역을 활성화하고 클릭하면 ref가 current: div로 바뀌고 다시 클릭하면 current: null이 된다.
useEffect(() => {
const listener: listenerType = (e) => {
if (e.target !== outsideRef.current) return;
//클릭한 요소와 모달 외부 영역이 일치한다면 handler을 수행함.
//여기서는 dispatch(closeModal())에 해당함.
handler(e);
};
document.addEventListener("mousedown", listener);
return () => {
document.removeEventListener("mousedown", listener);
//이벤트 종료
};
}, [outsideRef]);
useModal.ts
import { useEffect, RefObject } from "react";
type listenerType = (this: Document, ev: MouseEvent) => void;
type useModalType = (
outsideRef: RefObject<HTMLDivElement>,
handler: (event: MouseEvent) => void
) => void;
export const useModal: useModalType = (outsideRef, handler) => {
useEffect(() => {
const listener: listenerType = (e) => {
if (e.target !== outsideRef.current) return;
handler(e);
};
document.addEventListener("mousedown", listener);
return () => {
document.removeEventListener("mousedown", listener);
};
}, [outsideRef]);
};
useModal(모달을 감싸는 div의 ref, () => 클릭 시 실행될 함수);
'HTMLDivElement' 형식에 'current' 속성이 없습니다
아래 코드에 에러가 발생했다.
type useModalType = (outsideRef: HTMLDivElement, handler: any) => void;
if (e.target !== outsideRef.current) return;//에러발생
outsideRef는 state 자체가 아니라 current를 다루는 'useRef'이기 때문에 HTMLDivElement으로만 단순히 타입을 지정해서는 안되고 HTMLDivElement을 useRef의 객체 타입의 제네릭 값으로 넘겨줘야 한다.
interface MutableRefObject<T> {
current: T;
}
interface RefObject<T> {
readonly current: T | null;
}
useModal.ts
type useModalType = (
outsideRef: RefObject<HTMLDivElement>,
handler: (event: MouseEvent) => void
) => void;
square.ts
outside의 추론 타입도 React.MutableRefObject로 바꿔주었다.
const outside = useRef() as React.MutableRefObject<HTMLDivElement>;
참고
https://driip.me/7126d5d5-1937-44a8-98ed-f9065a7c35b5
메모장 테마 분리 - text, 코드 블럭, todo