웹페이지를 구현하다보면 드롭다운을 만들 일이 많다.
사용자에게 값을 선택하게 하는 드롭다운은 하나의 서비스에서도 다양하게 그리고 자주 쓰이는 것을 볼 수 있다.
HTML 태그로서는 select
과 option
태그로 구현하거나 커스텀을 위해서 다른 태그로 구현한 경험은 있지만,
React 에서는 React에서 구현하는 만큼 커스텀 훅으로 구현하고 싶었다.
구현하고 싶은 드롭다운의 기능은 다음과 같다.
- 커스텀 훅으로 구현
- 마우스 호버가 아닌 클릭 시
- 드롭다운 밖을 클릭 시 드롭다운이 안보이게
- 사용자가 값을 선택하면 드롭다운이 안보이게
나는 회원가입 페이지에서 사용자의 전화번호 입력 부분을 드롭다운으로 구현하고 싶었고,
구현 완료된 모습은 다음과 같다.
우선, useState
를 통해 isOpen(Boolean 값)
이라는 상태를 만든 후 메뉴를 클릭, 혹은 메뉴 외의 구역을 클릭 시 안보이게 해보자.
커스텀 훅의 코드는 다음과 같다.
import { useState, useEffect } from 'react';
const useDetectClose = (ref, initialState) => {
const [isOpen, setIsOpen] = useState(initialState);
useEffect(() => {
const pageClickEvent = e => {
if (ref.current && !ref.current.contains(e.target)) {
setIsOpen(!isOpen);
}
};
if (isOpen) {
window.addEventListener('click', pageClickEvent);
}
return () => {
window.removeEventListener('click', pageClickEvent);
};
}, [isOpen, ref]);
return [isOpen, setIsOpen];
}
export default useDetectClose;
ref
와 initialState
값을 인자로 전달받게 코드를 작성했고
useEffect dependecy
에 isOpen
을 넣어줌으로써 isOpen
이 변할 때마다 함수를 실행시키는 구조이다.
그리고 isOpen
의 값에 따라 전역 객체에 이벤트를 달아 주어
사용자가 클릭한 요소 안의 요소인지 확인 후 닫아주는 구조이다.
마지막으로 해당 component가 unmount 될 때 이벤트 핸들러를 제거해준다. (useEffect return
부분)
그럼 위의 커스텀 훅을 회원가입 페이지에 구현해 보자.
import { useRef, useEffect } from 'react';
import useDetectClose from '../hooks/useDetectClose';
import { PhoneDropDown } from '../components/PhoneDropDown';
export const Join = () => {
const dropDownRef = useRef();
const [phoneIdentify, setPhoneIdentify] = useState('');
const phoneList = [] // ['010', '011', '017', ...]
const [isOpen, setIsOpen] = useDetectClose(dropDownRef, false);
...
return(
...
<div ref={dropDownRef}>
<input
onClick={() => setOpen(!isOpen)}
type='button'
value={phoneIdentify}
/>
...
{isOpen &&
<ul>
{phoneList.map((value, index) => (
<PhoneDropDown key={index} value={value} setIsOpen={setIsOpen} setPhoneIdentify={setPhoneIdentify} isOpen={isOpen} />
))}
</ul>
...
}
</div>
)
}
드롭다운 이외의 코드는 모두 제거했다.
useRef
를 통해 사용자가 클릭한 요소에 접근할 수 있도록 한다.
그리고 해당 ref
값은 사용자가 클릭한 요소 안의 요소인지 확인하는 용도로 쓰인다.
마지막으로 사용자가 드롭다운의 값을 선택하면 해당 값을 UI에 보이게 해야한다.
예를 들어, 사용자가 010
을 선택하면 드롭다운은 없어지고 input
의 value
에 010
이 들어가야 한다.
useState
으로 간단하게 구현가능하다.
해당 내용에 대한 코드는 다음과 같다.
export const PhoneDropDown = ({ value, setPhoneIdentify, setIsOpen, isOpen }) => {
const ValueClick = () => {
setPhoneIdentify(value)
setIsOpen(!isOpen)
}
return(
<li onClick={ValueClick}>{value}</li>
)
}