1주일 배운 typescript로 어찌저찌 props type받아와서 select버튼을 직접 만들어보았다.
appjam프로젝트에서 내가 맡은 부분이 강의리스트들을 서버로부터 받아와서 gui를 구현하고, 해당 강의를 기준에 따라 정렬할 수 있는 sorting 버튼을 만드는 것이었다.
다행히도 정렬 기준은 5개였지만 정렬은 중복을 허용하지 않았다!😂
정렬 버튼들은 모습이 select태그와 하는 기능이 똑같았지만, select태그는 커스텀의 제한이 있기 때문에 최종적으로 button태그와 ul+li태그로 구현하게 되었다.
button태그를 이용해서 직접 select 버튼을 만들고 있었는데, 해야할 것들이 다음과 같았다.
총 소요시간, 가격, 개설일, 반복시청기간, 질의응답 시간에 따라 각각 드랍다운 메뉴가 달랐기 때문에
<SortingBtn>총 소요시간</SortingBtn>
<SortingBtn>가격</SortingBtn>
<SortingBtn>...</SortingBtn>
<SortingBtn>총 소요시간</SortingBtn>
이렇게 일일이 넣어주려다가 그 밑 dropdown 메뉴가 각 버튼마다 다르게 나와야하기때문에 각각 버튼 기준과 그 아래 dropdown메뉴 이름을 객체에 넣어놓고 관리하기로 했다.
return (
<div>
{sortingCriteria.map((criteria) => (
<SortingBtn
key={criteria}
value={criteria}
dropListName={dropListName}
onClickOpenSorting={handleOpenSorting}
isOpen={isOpen}
onClickSortingItem={handleClickSortingItem}
isSelected={isSelected}
selectedItem={selectedItem}
criteria={criteria}
>
{criteria}
</SortingBtn>
))}
</div>
);
}
interface SortingBtnProps {
value: sortingType;
children: React.ReactNode;
dropListName: IDropListName;
selectedItem: ISelectedItemName;
isOpen: ISorting;
isSelected: ISorting;
onClickOpenSorting: (criterial: sortingType) => void;
onClickSortingItem: (value: sortingType, item: string) => void;
criteria: sortingType;
}
...
function SortingBtn({
onClickOpenSorting,
onClickSortingItem,
isOpen,
isSelected,
dropListName,
selectedItem,
value,
criteria,
}: SortingBtnProps) {
return (
<StyledRoot onClick={() => onClickOpenSorting(criteria)}>
<BtnTextWrapper>
<CriteriaItem color={colors.gray6}>{value}</CriteriaItem>
{isSelected[value] && (
<>
<CriteriaItem>|</CriteriaItem>
<CriteriaItem color={colors.mainBlue}>{selectedItem[value]}</CriteriaItem>
</>
)}
</BtnTextWrapper>
{isOpen[value] ? <ArrowUp /> : <ArrowDown />}
{isOpen[value] && (
<DropDownBox>
{dropListName[value].map((item) => (
<DropDownItem key={item} onClick={() => onClickSortingItem(value, item)}>
{item}
</DropDownItem>
))}
</DropDownBox>
)}
</StyledRoot>
);
}
//isOpen 객체의 type정의
export interface ISorting {
[key: string]: boolean;
}
const sortingObject: ISorting = {};
sortingCriteria.forEach((element) => (sortingObject[element] = false));
const [isOpen, setIsOpen] = useState(sortingObject);```
isOpen state로 관리한다.
기본 값으로 들어가는 객체는 다음과 같다.
{ "총 소요시간": false,
"가격": false,
"개설일": false,
"반복시청 기간": false,
"질의응답 시간": false,
}
const handleOpenSorting = (criteria: sortingType) => {
switch (criteria) {
case criteria:
setIsOpen({
...sortingObject,
[criteria]: !isOpen[criteria],
});
break;
}
};
export type sortingType = "총 소요시간" | "가격" | "개설일" | "반복시청 기간" | "질의응답 시간";
//dropdown 내리면 나오는 선택 목록들 type정의
export type IDropListName = {
[key in sortingType]: string[];
};
-> 모두 한 객체에 담아 관리하기
const isSelectedObject: ISorting = {};
const selectedItemName: ISelectedItemName = {};
sortingCriteria.forEach((element) => (isSelectedObject[element] = false));
sortingCriteria.forEach((element) => (selectedItemName[element] = ""));
const [isSelected, setIsSelected] = useState(isSelectedObject);
const [selectedItem, setSelectedItem] = useState(selectedItemName);
선택된 기준의 경우만 무엇이 선택되었는지 버튼 안에 표시되어야함.
중복 선택이 안되기 때문에 isOpen처럼 isSelected도 객체로.. 한번에 관리한다.
초기값은 모두 false.
중복 정렬기능이 없기 때문에 한 곳에서 선택했으면 나머지는 초기화되어야함
어떤 기준의 어떤 기준 목록을 선택했는지 저장 필요 ex){"가격": "높은 순", ...} 초기값은 모두 빈 문자열
isOpen과 같이 정렬 기준배열을 forEach문으로
const handleClickSortingItem = (value: sortingType, item: string) => {
switch (item) {
case item:
setSelectedItem({
...selectedItemName,
[value]: item,
});
setIsSelected({
...isSelectedObject,
[value]: true,
});
break;
}
};
드랍다운 아이템을 클릭하면 어떤 기준을 선택했고, 그 기준(value)의 어떤 목록(item)을 선택했는지 받아온다.
item이 '긴 순서' '짧은 순서'로 겹치는 경우가 있긴 한데 받아오는 value가 달라서 잘 작동되는 것 같다.
어떤 item이 선택되었는지 selectedItemd에 저장하고, 나머지는 빈 문자열로 초기화
어떤 기준(value)이 선택되었는지 true로 바꾸고 나머진 false로 초기화
typescript를 배운지 1개월도 되지 않은 상태로 프로젝트 진행중이라 얼렁뚱땅 한 것 같은데, 다행히 잘 돌아가는 걸 보면 이상은 없는 것 같다.
앱잼이 끝나면 typescript개념을 다시 익히면서 차근차근 배우고 어떻게 효율적인 코드를 작성할 수 있을지 고민해보면 좋을 것 같다.
킹지구님 쩔어여