enum
값만 받으면 enum의 길이만큼 Tab Item을 만들 수 있도록 사용할 수 있는 Tab이라는 공용 컴포넌트를 만들고 싶었다.
관심사를 분리(SoC)하기 위하여 tab을 사용할 부모 컴포넌트에서 선택된 값을 concern하기 위해 사용할 useTab
이라는 커스텀 훅을 작성하였다.
T
를 전달받아 state의 type을 지정한다initialValue
를 받아 state를 초기화한다.const useTab = <T>(initialValue: T) => {
const [selectedTab, setSelectedTab] = useState<T>(initialValue);
const handleSelectedTab = (tab: T) => {
setSelectedTab(tab);
};
return {
selectedTab,
handleSelectedTab,
};
};
export default useTab;
Tab
컴포넌트에서는 선택된 값과 state를 바꾸는 함수를 전달 받아 화면에 그려주고, interact(클릭 이벤트)에 대해 concern한다.
enum
을 전달 받아 배열화하여 TabItem만큼 반복문을 실행한다.enum
의 value를 state 바꾸는 함수에 전달한다.interface Props {
tabEnum: Record<string, string>;
tabItemWidth?: number;
selectedTab: string;
handleSelectedTab: (tab: string) => void;
}
const Tab: React.FC<Props> = props => {
const { tabEnum, tabItemWidth, selectedTab, handleSelectedTab } = props;
const enumArray = objectToArray(tabEnum); //enum을 array로 만드는 util
return (
<Wrapper>
{enumArray.map(([key, value]) => (
<TabItem
key={key}
enumKey={key}
enumValue={value as string}
tabItemWidth={tabItemWidth}
isSelected={value === selectedTab}
handleSelectedTab={handleSelectedTab}
/>
))}
</Wrapper>
);
};
const Wrapper = styled(Row)`
width: 100%;
justify-content: flex-start;
align-items: center;
`;
위 코드에서 문제는 부모 컴포넌트에서 Tab
에 handleSelectedTab
을 전달하고자 할 때 발생하였다.
T
와 Tab
컴포넌트의 handleSelectedTab
의 props인 string
의 type이 맞지 않아서 typescript 에러가 발생하였다.handleSelectedTab
의 props의 type을 any로 하는 것이다.그래서 아래와 같이 수정하게 되었다.
interface Props<T> {
tabEnum: T;
tabItemWidth?: number;
selectedTab: T;
handleSelectedTab: (tab: T) => void;
}
export type TabComponentInterface<T = any> = React.FC<Props<T>>; //추가
const Tab: TabComponentInterface = props => {
const { tabEnum, tabItemWidth, selectedTab, handleSelectedTab } = props;
const enumArray = objectToArray(tabEnum); //enum을 array로 만드는 util
return (
<Wrapper>
{enumArray.map(([key, value]) => (
<TabItem
key={key}
enumKey={key}
enumValue={value as string}
tabItemWidth={tabItemWidth}
isSelected={value === selectedTab}
handleSelectedTab={handleSelectedTab}
/>
))}
</Wrapper>
);
};
const Wrapper = styled(Row)`
width: 100%;
justify-content: flex-start;
align-items: center;
`;
export default Tab;
Type
을 전달 받아 interface 내에서 사용할 수 있도록 하였다.TabComponentInterface
type을 만들어 Type
을 전달 받을 수 있도록 하고, export 하여 부모 컴포넌트에서도 사용할 수 있게 하였다.Tab
컴포넌트를 TabComponentInterface
type으로 지정하였다.const TabComponent = Tab as TabComponentInterface<TempEnum>;
...
const Presenter: React.FC<Props> = props => {
...
return(
...
<TabComponent
tabEnum={TempEnum}
handleSelectedTab={handleSelectedTab}
selectedTab={selectedTab}
/>
...
)
}
Tab
컴포넌트를 as TabComponentInterface<TempEnum>
으로 TempEnum
type을 전달하도록 type 지정을 하였고, TabComponent
를 실제 return에서 JSX.element
로 사용하였다.