[React] 드롭다운 구현하기

Hyuk·2023년 1월 1일
3

웹페이지를 구현하다보면 드롭다운을 만들 일이 많다.
사용자에게 값을 선택하게 하는 드롭다운은 하나의 서비스에서도 다양하게 그리고 자주 쓰이는 것을 볼 수 있다.
HTML 태그로서는 selectoption 태그로 구현하거나 커스텀을 위해서 다른 태그로 구현한 경험은 있지만,
React 에서는 React에서 구현하는 만큼 커스텀 훅으로 구현하고 싶었다.


구현하고 싶은 드롭다운의 기능은 다음과 같다.

  1. 커스텀 훅으로 구현
  2. 마우스 호버가 아닌 클릭 시
  3. 드롭다운 밖을 클릭 시 드롭다운이 안보이게
  4. 사용자가 값을 선택하면 드롭다운이 안보이게

나는 회원가입 페이지에서 사용자의 전화번호 입력 부분을 드롭다운으로 구현하고 싶었고,
구현 완료된 모습은 다음과 같다.

구현 모습

구현 방법

우선, 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;

refinitialState 값을 인자로 전달받게 코드를 작성했고
useEffect dependecyisOpen 을 넣어줌으로써 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 을 선택하면 드롭다운은 없어지고 inputvalue010이 들어가야 한다.
useState 으로 간단하게 구현가능하다.

해당 내용에 대한 코드는 다음과 같다.

export const PhoneDropDown = ({ value, setPhoneIdentify, setIsOpen, isOpen }) => {
    const ValueClick = () => {
        setPhoneIdentify(value)
        setIsOpen(!isOpen)
    }
    return(
        <li onClick={ValueClick}>{value}</li>
    )
}
profile
프론트엔드 개발자

0개의 댓글