컴포넌트의 DOM요소를 사용하고 싶을 때 className이나 id를 지정하여 DOM을 불러오고, 그에 맞는 처리를 진행할 수 있다.
하지만, useRef를 이용해서도 DOM요소에 대한 이벤트를 제어하거나 생성하는 것이 유용하기 때문에 이에 대한 글을 작성해 보려 한다.
useRef
는.current
프로퍼티로 전달된 인자(initialValue)로 초기화된 변경 가능한 ref 객체를 반환합니다. 반환된 객체는 컴포넌트의 전 생애주기를 통해 유지될 것입니다. (출처 : 리액트 공식 문서)
이해하기 쉬운 말로 정리된 것을 보니, useRef는 .current 프로퍼티에 변경 가능한 값을 담고 있는 “상자”이기 때문에, DOM요소도 담을 수 있는 것이다.
이번에 구현할 기능은 useRef를 사용하여, 마우스로 클릭한 부분이 내가 선택한 부분이 DOM의 영역 범위가 아니라면, 모달창을 닫는 것이다.
먼저, modal 컴포넌트 안에 useRef
를 이용하여 순수한 ref
객체를 생성해 보자.
아직은 따로 선택한 current 지정이 없기 때문에 undefined
상태이다.
만약 특정 DOM을 선택하게 되면, .current
값으로 특정 값을 알아낼 수 있다.
const useOnClickOutside = (ref, handler) => {
useEffect(() => {
const listener = (event) => {
if(!ref.current || ref.current.contains(event.target)){
return;
}
handler();
};
document.addEventListener('mousedown', listener);
document.addEventListener('touchstart', listener);
return () => {
document.removeEventListener('mousedown', listener);
document.removeEventListener('touchstart', listener);
}
});
return (
<div>
</div>
);
};
Custom Hook의 모습이다.
이벤트 리스너를 생성하여, 마우스로 누르거나 터치를 하였을 때 콜백 함수를 실행하도록 한다. 콜백 함수에서는 아무것도 클릭하지 않은 상태이거나, 모달창 안쪽을 클릭하면 리스터 함수를 리턴한다. 그러나, 모달창 바깥쪽을 클릭하게 되면 핸들러 함수가 수행된다.
useOnClickOutside 를 넘겨주는 방식을 보아서 핸들러 함수가 어떤 것이 넘어오는지 보자.
useOnClickOutside(ref, () => {setModalOpen(false)});
이렇게 전달하게 되면 현재 ref의 상태와 모달의 state를 관리하는 함수를 props로 넘겨줄 수 있다.
그리고 현재 ref를 적용하여 DOM을 가져오고 싶은 컴포넌트에 ref를 지정하면 된다.
<Modal ref = {ref}>
<ModalClose onClick = {() => {setModalOpen(false)}}>
X
</ModalClose>
</Modal>
...