[React + TS] TabButton 컴포넌트 만들기

복숭아는딱복·2024년 8월 29일
0

토이프로젝트 3

목록 보기
2/3
post-thumbnail

특정 경로에 따라 다른 페이지로 이동할 수 있는 탭버튼을 만들어보자. 스타일링은 emotion를 사용했다.

1. interface 정의

interface를 자세히보면 다음과 같다.

tabNames: 탭의 이름들을 담은 문자열 배열
tabLinks: userId를 인자로 받아 탭의 링크를 반환하는 함수들의 배열

2. useParams 훅을 사용하여 URL 파라미터에서 userId 가져오기

  const { userId } = useParams<{ userId: string }>();

탭을 클릭하면 아래와 같은 경로들로 이동하게 할 것이다.

/playlist/userID
/playlist/userID/saved
/playlist/userID/liked

3. tabNames를 매핑하여 각 탭에 대한 NavLink를 생성

NavLink는 다음과 속성들을 가진다. NavLink는 isActive, isPending, isTransitioning 상태를 제공하여 쉽게 스타일링을 할 수 있도록 도와준다.
https://reactrouter.com/en/main/components/nav-link

to: NavLink가 연결할 경로를 나타낸다 (tabLinks 함수를 사용하여 생성)
className: 활성 상태에 따라 'active' 클래스를 추가한다
end: 첫 번째 탭에만 적용되어 정확한 경로 일치를 확인한다

end prop이 true로 설정된 경우, NavLink는 현재 URL이 정확히 해당 링크의 경로와 일치할 때만 "active" 상태가 된다. 최종 코드에서 end={index === 0}는 첫 번째 탭(index가 0인 탭)에만 end prop을 적용한다.

첫 번째 탭에 end를 사용함으로써, 이 탭이 다른 모든 하위 경로에서 활성화되는 것을 방지한다. 왜냐하면 위에서와 같이 첫번째 탭을 제외한 나머지 탭들은 하위 페이지기 때문이다.

/playlist/userID
/playlist/userID/saved
/playlist/userID/liked

즉 /playlist/123에서는 첫 번째 탭만 활성화되고, /playlist/123/saved에서는 두 번째 탭이 활성화된다.
/playlist/123/likes에서는 세 번째 탭이 활성화된다.

최종코드

import React from 'react';
import { css } from '@emotion/react';
import { NavLink, useParams } from 'react-router-dom';
import colors from '@/constants/colors';
import { fontWeight } from '@/constants/font';

interface TabButtonProps {
  tabNames: string[];
  tabLinks: ((userId?: string) => string)[];
}

const TabButton: React.FC<TabButtonProps> = ({ tabNames, tabLinks }) => {
  const { userId } = useParams<{ userId: string }>();

  return (
    <div css={tabContainerStyle}>
      {tabNames.map((name, index) => (
        <NavLink
          key={name}
          to={tabLinks[index](userId)}
          css={tabLinkStyle}
          className={({ isActive }) => (isActive ? 'active' : '')}
          end={index === 0}
        >
          {name}
        </NavLink>
      ))}
    </div>
  );
};

사용법

나는 tabLinks를 ROUTES에서 가져왔다.

  <TabButton
      tabNames={['마이플리', '저장된플리', '좋아요']}
      tabLinks={[ROUTES.PLAYLIST, ROUTES.PLAYLIST_SAVED, ROUTES.PLAYLIST_LIKES]}
    />
const ROUTES = {
  ROOT: '/',
  SIGN_IN: '/signIn',
  SIGN_UP: '/signUp',
  HASH_TAG: '/hashTag',
  POPULAR: '/popular',
  FOLLOWING: '/following',
  SEARCH: (searchText = ':searchText') => `/search/${searchText}`,
  DETAIL: (playListId = ':playListId') => `/detail/${playListId}`,
  FOLLOWER: (userId = ':userId') => `/follower/${userId}`,
  PROFILE: (userId = ':userId') => `/profile/${userId}`,
  PROFILE_MODIFY: (userId = ':userId') => `/profile/${userId}/modify`,
  PROFILE_FOLLOWING: (userId = ':userId') => `/profile/${userId}/following`,
  PLAYLIST: (userId = ':userId') => `/playlist/${userId}`,
  PLAYLIST_SAVED: (userId = ':userId') => `/playlist/${userId}/saved`,
  PLAYLIST_LIKES: (userId = ':userId') => `/playlist/${userId}/likes`,
  PLAYLIST_ADD: (userId = ':userId') => `/playlist/${userId}/add`,
  PLAYLIST_MODIFY: (userId = ':userId') => `/playlist/${userId}/modify`,
  NOT_FOUND: '*',
};

export default ROUTES;

0개의 댓글