[ React ] 기본 SelectBox 컴포넌트 구현

2024. 7. 3. 15:24· React
목차
  1. 요구사항 분석
  2. 구현
  3. 구조
  4. Props
  5. 1. OptionListBox Open/Close
  6. 2. 상위 state 변경
  7. 3. CaretIcon
  8. 4. 외부 영역 클릭 시 OptionListBox 닫기
  9. 5. 상태별 OptionItem 스타일링
  10. 결과
728x90

단순한 SelectBox 만들기 ~

 

 

요구사항 분석

필요한 컴포넌트

  • SelectorBox - 선택된 값이 표시되는 요소
  • OptionListBox - 옵션 리스트 팝업
  • OptionItem - 옵션 리스트 내의 각각의 아이템
  • CaretIcon - 옵션 리스트 팝업의 열림/닫힘을 표시하는 아이콘

 

구현할 동작

  1. SelectorBox를 누르면 OptionListBox가 열린다.
  2. OptionListBox에서 특정 OptionItem을 선택하면 OptionListBox가 닫히면서 SelectorBox에 선택한 값을 표시한다. (상위 데이터 변경)
  3. OptionListBox의 상태에 따라 CaretIcon을 바꿔준다.
  4. 외부 영역을 클릭했을 시에도 OptionListBox를 닫아준다.
  5. 마우스 hover 상태의 OptionItem은 밝은 색상의 배경색을 부여한다.
  6. 선택된 OptionItem은 글자색과 배경색으로 표시해준다.

 


 

구현

구조

<SelectorContainer>
  <CommonSelectorBox>
    {isClose ? (
      <FaCaretDown />
    ) : (
      <FaCaretUp />
    )}
  </CommonSelectorBox>
  {isOpen && (
    <CommonOptionListBox>
      {options.map((option, idx) => (
        <CommonOptionItem />
      ))}
    </CommonOptionListBox>
  )}
</SelectorContainer>

 

 

Props

export interface CommonSelectorProps {
  item: string;
  options: string[];
  placeholder?: string;
  onChange: (item: string) => void;
}
  • item: 선택된 옵션 값
  • options: 옵션 리스트
  • placeholder: 선택된 옵션 값이 없을 경우 SelectorBox에 표시할 문구
  • onChange: 상위에서 item 값을 바꿔주기 위한 함수

 

 

1. OptionListBox Open/Close

옵션 리스트 팝업을 열고 닫는 동작이 더욱 자연스럽도록 애니메이션을 추가해줬다.

animation: ${fadeIn} 0.3s ease forwards;

다만 OptionListBox를 열 때는 애니메이션이 정상적으로 동작하지만 닫을 때는 isOpen 변수가 변경되며 OptionListBox가 바로 언마운트되므로 애니메이션 효과가 적용되지 않는다.

그래서 isOpen 변수와 isClose 변수를 따로 분리하여 닫힘 시에도 애니메이션을 적용하고자 했다.

 

즉, 닫을 때 동작은

  1. isClose 변수가 true로 변경
  2. isClose 조건부로 fadeOut 애니메이션 실행
  3. 0.3초 이후 isOpen 변수가 false로 변경되며 OptionListBox 컴포넌트 언마운트
const handlePopup = () => {
  if (isOpen) {
    setIsClose(true);
    setTimeout(() => {
      setIsOpen(false);
    }, 300);
  } else {
    setIsClose(false);
    setIsOpen(true);
  }
};
export const OptionListBox = styled.div<{ $isclose?: boolean }>`
	...
  animation: ${fadeIn} 0.3s ease forwards;

  ${({ $isclose }) =>
    $isclose &&
    css`
      animation: ${fadeOut} 0.3s ease forwards;
    `}
`;

 

 

2. 상위 state 변경

react는 단방향 데이터 흐름으로 상위의 state를 하위에서 변경하는 것이 불가능하다.

대신 상위의 함수를 하위 컴포넌트에 props로 전달해 상위의 state를 변경할 수 있다.

즉, OptionItem을 선택했을 때 props로 받은 onChange 함수에 선택한 값을 파라미터로 전달하여, 상위 컴포넌트의 onChange 함수에서 item 값을 변경한다.

<CommonOptionListBox $isclose={isClose}>
  {options.map((option, idx) => (
    <CommonOptionItem
      key={idx}
      onClick={() => handleClick(option)}
    >
      {option}
    </CommonOptionItem>
  ))}
</CommonOptionListBox>
const handleClick = (item: string) => {
  onChange(item);  // props로 전달받은 함수 onChange
  handlePopup();
};

 

 

3. CaretIcon

isClose 값에 따라 조건부로 렌더링해줬다.

{isClose ? (
  <FaCaretDown className="caret-icon" />
) : (
  <FaCaretUp className="caret-icon" />
)}

 

 

4. 외부 영역 클릭 시 OptionListBox 닫기

 

useRef로 영역 외 클릭 시 감지 기능 구현하기

useRef렌더링에 필요하지 않은 값을 참조할 수 있는 React Hook useRef – ReactThe library for web and native user interfacesko.react.dev useRef는 처음에 제공한 초기값으로 설정된 단일 current 프로퍼티가 있는 ref 객

zodev.tistory.com

지난번 Datepicker를 구현할 때와 동일하게 진행했다.

const handleClickOutside = (e: MouseEvent) => {
  if (
    selectorRef.current &&
    !selectorRef.current.contains(e.target as Node) &&
    isOpen
  ) {
    handlePopup();
  }
};

  useEffect(() => {
    document.addEventListener("click", handleClickOutside);
    return () => {
      document.removeEventListener("click", handleClickOutside);
    };
  }, [selectorRef.current]);

 

 

5. 상태별 OptionItem 스타일링

export const OptionItem = styled.div<{
  $disabled?: boolean;
  $selected?: boolean;
  $focused?: boolean;
}>`
  ...
  
  ${({ $disabled }) =>
    $disabled &&
    css`
      color: #c9c9c9;
      background-color: #f7f7f7;
      cursor: not-allowed;
    `}

  ${({ $selected }) =>
    $selected &&
    css`
      background-color: ${({ theme }) => theme.teamColors.blueSoft};
      color: ${({ theme }) => theme.teamColors.blueHard};
      font-weight: 500;
    `}

  &:hover {
    background-color: rgb(24, 144, 255, 0.05);
  }
`;
  • 비활성화된 옵션 → 선택 불가, 회색 처리
  • 선택된 옵션 → 파란색 글자 + 배경색
  • 마우스 hover된 옵션 → 연한 파란색 배경

 

 


 

결과

 

 

 

 

 

 

728x90

'React' 카테고리의 다른 글

[ React ] input 태그의 placeholder로 icon 사용하기  (0) 2024.07.10
[ React ] 검색 가능한 SelectBox 컴포넌트 구현  (0) 2024.07.04
[ React ] TimeTable 시간표 컴포넌트 구현  (0) 2024.06.24
[ React ] weekSelector 컴포넌트 구현  (0) 2024.06.21
[ React ] useRef로 영역 외 클릭 시 감지 기능 구현하기  (0) 2024.06.19
  1. 요구사항 분석
  2. 구현
  3. 구조
  4. Props
  5. 1. OptionListBox Open/Close
  6. 2. 상위 state 변경
  7. 3. CaretIcon
  8. 4. 외부 영역 클릭 시 OptionListBox 닫기
  9. 5. 상태별 OptionItem 스타일링
  10. 결과
'React' 카테고리의 다른 글
  • [ React ] input 태그의 placeholder로 icon 사용하기
  • [ React ] 검색 가능한 SelectBox 컴포넌트 구현
  • [ React ] TimeTable 시간표 컴포넌트 구현
  • [ React ] weekSelector 컴포넌트 구현
ZoD
ZoD
바쁘게 굴러가는 ZoD의 하루~
como siempre바쁘게 굴러가는 ZoD의 하루~
ZoD
como siempre
ZoD
  • 분류 전체보기 (173)
    • Daily (0)
    • Javascript (16)
      • 모던 자바스크립트 (46)
    • Typescript (8)
    • FrontEnd (7)
    • React (21)
      • tanstack query (1)
      • 라이브러리 (3)
    • Vue (33)
      • Nuxt (11)
      • Quasar (6)
    • CSS (12)
    • Storybook (8)
    • Figma (3)
    • CS 지식 (5)
    • Network (5)
    • 알고리즘 (2)
    • 사이드 프로젝트 (4)
      • ddangkong (4)
      • boncierge (0)
    • 개발, 근데 이제 헛소리를 곁들인 (1)
    • ETC (2)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

인기 글

최근 글

hELLO · Designed By 정상우.v4.2.2
ZoD
[ React ] 기본 SelectBox 컴포넌트 구현
상단으로

티스토리툴바

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.