<sortTab>
컴포넌트는 위 이미지와 같이 정렬의 기준을 설정하는 탭 메뉴 성격의 컴포넌트이다. 원하는 정렬 기준을 클릭하면 path (혹은 query string)이 변경된다. 그리고 해당 정렬 기준에 맞는 정보를 보여주고 활성화된 탭에는 css 강조 효과를 보여 주는 역할을 한다.
클릭으로 path를 전환하고 그에 맞는 css 효과를 적용하는 것은 어렵지 않았지만, 맨 처음 해당 페이지로 이동하고 정렬 기준을 설정하지 않았을 때에도 둘 중 하나의 기준을 디폴트로 정해 정렬하고, 해당 기준의 탭에도 css 강조 효과를 주는 부분에서 좀 헤맸었다.
먼저는 useState
를 사용해서 초기값을 디폴트 정렬로 지정하고 싶은 path로 지정하고 정렬 기준을 클릭할 때마다 state를 업데이트하여 전환하고 Link로 해당 경로로 이동하는 방법을 사용했었다.
이 방법은 얼핏 잘 작동하는 것처럼 보였으나 문제가 있었다. 뒤로 가기를 할 때는 path를 추적을 하지 못하는 것이었다.. 실제 url 경로를 따르지 않고 클릭할 때만 변환되는 state를 추적하니 생기는 문제였다.
그래서 state 대신 실제 url 경로를 따르는 방법으로 변경해야 했다. 또한 하나의 <SortTab>
을 재활용 해서 path parameter 뿐만 아니라 query string을 기준으로도 동일한 기능이 작동하게 만들고 싶었다.
결론적으로, 아래와 같이 작동하도록 코드를 변경했다.
<SortTab>
를 동일하게 사용해 path parameter 뿐만 아니라 query string을 기준으로도 같은 기능을 할 수 있도록 할 것// ...
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>
}
// ...
// ...
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>
);
}
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>
);
}