번개 모임 웹 어플리케이션 - sortTab 컴포넌트 관련 에러 수정 및 리팩토링

선정·2023년 5월 16일
0
post-custom-banner

sortTab 컴포넌트 관련 에러

<sortTab> 컴포넌트는 위 이미지와 같이 정렬의 기준을 설정하는 탭 메뉴 성격의 컴포넌트이다. 원하는 정렬 기준을 클릭하면 path (혹은 query string)이 변경된다. 그리고 해당 정렬 기준에 맞는 정보를 보여주고 활성화된 탭에는 css 강조 효과를 보여 주는 역할을 한다.

클릭으로 path를 전환하고 그에 맞는 css 효과를 적용하는 것은 어렵지 않았지만, 맨 처음 해당 페이지로 이동하고 정렬 기준을 설정하지 않았을 때에도 둘 중 하나의 기준을 디폴트로 정해 정렬하고, 해당 기준의 탭에도 css 강조 효과를 주는 부분에서 좀 헤맸었다.

먼저는 useState를 사용해서 초기값을 디폴트 정렬로 지정하고 싶은 path로 지정하고 정렬 기준을 클릭할 때마다 state를 업데이트하여 전환하고 Link로 해당 경로로 이동하는 방법을 사용했었다.
이 방법은 얼핏 잘 작동하는 것처럼 보였으나 문제가 있었다. 뒤로 가기를 할 때는 path를 추적을 하지 못하는 것이었다.. 실제 url 경로를 따르지 않고 클릭할 때만 변환되는 state를 추적하니 생기는 문제였다.

그래서 state 대신 실제 url 경로를 따르는 방법으로 변경해야 했다. 또한 하나의 <SortTab>을 재활용 해서 path parameter 뿐만 아니라 query string을 기준으로도 동일한 기능이 작동하게 만들고 싶었다.

결론적으로, 아래와 같이 작동하도록 코드를 변경했다.

  1. 뒤로 가기를 할 때도 url이 정상적으로 변경되고 그에 따라 css 효과가 동적으로 변경될 것
  2. 탭 메뉴를 선택하기 전, 기준이 되는 path가 없을 때는 디폴트가 되는 정렬 기준을 정해 활성화 할 것
  3. <SortTab>를 동일하게 사용해 path parameter 뿐만 아니라 query string을 기준으로도 같은 기능을 할 수 있도록 할 것


ProfilePage 컴포넌트

  • tabMenu 객체 수정
    • 객체의 key 변경 (SortTab에서 path parameter 뿐만 아니라 query string도 props로 받을 것이기 때문에 보다 일반적인 명칭으로 변경)
    • 경로 추적을 위한 sortBy 속성과 경로 이동을 위한 linkTo 분리
  • useState 대신 url useLocation를 사용해 경로 추적
    • useState 대신 pathname 추적
    • 페이지 전환을 위해 useNavigate 사용 (SortTab에서 Link를 사용하지 않을 것이기 때문에)

- 변경 전

// ...

function ProfilePage() {
  // ...
  
  const [sortPathname, setSortPathname] = useState("/profile/created");
  
    const tabMenu = [
    {
      name: "내가 만든 번개",
      pathname: "/profile/created"
    },
    {
      name: "내가 참여한 번개",
      pathname: "/profile/participated"
    }
  ];
  
  // tabMenu의 name을 클릭할 때마다 state 변경
   const switchTab = (selected) => {
    setSortPathname(selected);
  };
  
  // ...
  return (
    <RootPageContent>
      <UserInfo emoji="😶‍🌫️" nickname={nickname} email="test@test.com" />
      <UserBungaeList
        sortPathname={sortPathname}
        switchTab={switchTab}
        tabMenu={tabMenu}
        bungaeList={bungaeList}
      />
    </RootPageContent>
  );
}

// ...

- 변경 후

// ...

function ProfilePage() {
  // ...
  
  const navigate = useNavigate();
  const { pathname } = useLocation();
  
  const tabMenu = [
    {
      name: "내가 만든 번개",
      sortBy: ["/profile", "/profile/created"], // css 활성화 경로
      linkTo: "/profile/created" // 클릭 시, 이동 경로
    },
    {
      name: "내가 참여한 번개",
      sortBy: ["/profile/participated"], // css 활성화 경로
      linkTo: "/profile/participated" // 클릭 시, 이동 경로로
    }
  ];
  
  // tabMenu의 name을 클릭할 때마다 linkTo 경로로 이동
  const switchTabHandler = (selected) => {
    navigate(selected);
  };
  
  // ...
  return (
    <RootPageContent>
      <UserInfo emoji="😶‍🌫️" nickname={nickname} email="test@test.com" />
      <UserBungaeList
        sortBy={pathname}
        onSwitchTab={switchTabHandler}
        tabMenu={tabMenu}
        bungaeList={bungaeList}
      />
    </RootPageContent>
}

// ...


SortTab 컴포넌트

  • props명 변경 (path parameter 뿐만 아니라 query string도 props로 받을 것이기 때문에 보다 일반적인 명칭으로 변경)
  • 경로 추적으로 css 효과를 주기 위해 tabMenu의 sortBy 배열을 확인(includes 메서드 사용)
  • Link 컴포넌트(a 태그) 대신 li 태그 사용
    • path parameter 뿐만 아니라 query string을 기준으로 정렬할 때를 고려

- 변경 전

// ...

function SortTab({ sortPathname, switchTab, tabMenu }) {
  return (
    <StyledSortTab>
      {tabMenu.map((menu) => (
        <Link
          to={menu.pathname}
          key={menu.name}
          onClick={() => switchTab(menu.pathname)}
        >
          <div className="user-tab">
            <div>{menu.name}</div>
          </div>
          {menu.pathname === sortPathname && <div className="underscore"></div>}
        </Link>
      ))}
    </StyledSortTab>
  );
}

// ...

- 변경 후

function SortTab({ sortBy, onSwitch, tabMenu }) {
  return (
    <StyledSortTab>
      {tabMenu.map((menu) => (
        <li
          role="menuitem"
          key={menu.name}
          onClick={() => onSwitch(menu.linkTo)}
        >
          <div className="tab-menu">
            <div>{menu.name}</div>
          </div>
          {menu.sortBy.includes(sortBy) && <div className="underscore"></div>}
        </li>
      ))}
    </StyledSortTab>
  );
}

BungaeSearchPage

  • query string를 기준으로 탭 전환
  • react-router의 useSearchParams 이용

pages/BungaeSearch.js

// ...

function BungaeSearchPage() {
  // ...
  
  const [searchParams, setSearchParams] = useSearchParams();
  const sort = searchParams.get("sort");
  
  export const tabMenu = [
  {
    name: "최신순",
    sortBy: [null, "newest"],
    linkTo: "newest"
  },
  {
    name: "마감임박순",
    sortBy: ["last-minute"],
    linkTo: "last-minute"
  }
];
  
  // ...

  const switchTabHandler = (selected) => {
    setSearchParams({ sort: selected });
  };
  
  // ...

  return (
    <RootPageContent>
    // ...
    
      <StyledSection>
        <SearchedBungaeList
          count={bungaeList.length}
          sortBy={sort}
          onSwitchTab={switchTabHandler}
          tabMenu={tabMenu}
          bungaeList={bungaeList}
        />
      </StyledSection>
    </RootPageContent>
  );
}

// ...

components/BungaeSearch/SearchedBungaeList.js

function SearchedBungaeList({
  count,
  sortBy,
  onSwitchTab,
  tabMenu,
  bungaeList
}) {
  return (
    <section>
      <StyledHeadingWrapper>
        <h1>{`번개 검색 결과 (${count})`}</h1>
        <SortTab sortBy={sortBy} onSwitch={onSwitchTab} tabMenu={tabMenu} />
      </StyledHeadingWrapper>
      <BungaeListContent bungaeList={bungaeList} />
    </section>
  );
}
profile
starter
post-custom-banner

0개의 댓글