💁🏻♀️ too long? click HERE
🚨 문제 상황
컴포넌트화 한 svg 아이콘을 상수화해서 객체의 프로퍼티 값으로 저장해 두었다. 여기까지는 해결을 했는데 이어지는 문제는 이 컴포넌트에 프롭을 어떻게 내려주어야 하는지.. 상수화를 위해 만든 객체 구조는 대략 이렇다. (전체 코드는 이전 포스트에)
export const BOARDS: Boards = {
dashboard: {
boardName: '대시보드',
icon: <BoxIcon />,
link: '/main',
},
}
구조분해 문법을 사용해서 해당 svg 아이콘을 가지고 있는 icon 프로퍼티의 값을 가져온다. 적절한 자리에 {icon}을 넣어 주긴 했는데..
function BoardItem({ boardType, pathname }: BoardItemProps) {
const { boardName, icon, link } = BOARDS[boardType as keyof Boards];
const isCurrent = pathname === link ? true : false;
return (
<Link to={link}>
<button
className={clsx(
'flex h-16 w-[21.2rem] items-center gap-[1.6rem] rounded-[0.6rem] px-[1.6rem] py-[0.8rem] hover:bg-[#EDEEDC]/10',
isCurrent && 'bg-[#EDEEDC]/10',
)}
>
{icon} // 해당 svg 컴포넌트
<span className="text-body3-regular text-[#EDEEDC] opacity-100">{boardName}</span>
</button>
</Link>
);
}
그래서 브라우저 ui에 알맞은 아이콘이 보이기는 하는데..
사실 이 컴포넌트에 프롭으로 넘겨줘야 할 값들이 있다. active 여부도 프롭으로 내려줘야 되고 active가 false인 경우에는 아이콘의 opacity값을 적용하는 className도 넘겨줘야 한다. 그런데 지금 상황은 그런 프롭들이 하나도 적용이 되지 않은 상황. constants.tsx 파일에서 넘겨주는 건 active 여부를 알 수 없으니 어려울 것 같고..
export const BOARDS: Boards = {
dashboard: {
boardName: '대시보드',
icon: <BoxIcon active={???}/>,
link: '/main',
},
}
어떻게 해야 할까?
🤗 문제 해결
cloneElement를 사용했다. cloneElement를 사용하면 element를 기준으로 새로운 React 엘리먼트를 만들 수 있다.
const clonedElement = cloneElement(element, props, ...children)
리액트 문서의 예제코드를 보면 이해하기 쉽다.
내 코드에 적용해 보면
function BoardItem({ boardType, pathname }: BoardItemProps) {
const { boardName, icon, link } = BOARDS[boardType as keyof Boards];
const isCurrent = pathname === link ? true : false;
const boardItemIcon = icon as ReactElement; // ReactElement로 타입 지정
return (
<Link to={link}>
<button
className={clsx(
'flex h-16 w-[21.2rem] items-center gap-[1.6rem] rounded-[0.6rem] px-[1.6rem] py-[0.8rem] hover:bg-[#EDEEDC]/10',
isCurrent && 'bg-[#EDEEDC]/10',
)}
>
{icon &&
cloneElement(boardItemIcon, { // 여기!
...boardItemIcon.props,
active: isCurrent,
fill: 'white',
className: clsx(isCurrent || 'opacity-10'),
})}
<span className="text-body3-regular text-[#EDEEDC] opacity-100">{boardName}</span>
</button>
</Link>
);
}
내 코드에서 cloneElement를 사용해서 만든 컴포넌트는 아래와 같은 컴포넌트인 것. 컴포넌트 명은 BoardItem 컴포넌트의 프롭을 통해 내려온 boardType에 따라 달라지는 변수다.
// 참고로 tailwind CSS 사용 중
<svg컴포넌트 active={isCurrent} fill="white" className={clsx(isCurrent || 'opacity-10')}/>
이렇게 의도한 대로 잘 보인다!
아주 귀엽다~
그런데 사실..
리액트 문서는 cloneElement를 그다지 추천하지 않는다. 흔하지도 않고 취약하다고 함. 속한 카테고리도 레거시 카테고리다. 대신에 다른 대안을 좀 제시해 준다. 다른 방법도 시도해 봐야겠다. 글이 너무 길어지니 다음 포스트에 기록하겠다.
🧠 배우고 느낀 점
1. cloneElement를 사용하면 컴포넌트명을 동적으로 사용할 수 있다.
그런데 이제 레거시 코드라는 것. 그래서 다른 대안을 선택하는 편이 좋다는 것. 그런데 멘토님께서 이 방법을 알려주신 걸 보면 실무에서 많이 쓰는 것 같기도 하다. 다음번에 한번 다시 질문드려봐야겠다.
2. 리액트 문서를 읽자.
리액트를 가장 리액트답게 사용하려면 리액트에서 제공해 주는 가이드를 따라 학습하는 게 가장 좋은 방법이 아닐까. 리액트 문서가 잘 되어 있기도 하고. 사실 오늘 공부한 cloneElement 메서드도 오늘 처음 보았다. 리액트 문서를 좀 열심히 읽어보자.
'Frontend > React.js' 카테고리의 다른 글
[React] 리액트 컴포넌트를 값으로 사용하기 (0) | 2024.11.28 |
---|---|
[React] svg 컴포넌트 만들기 (0) | 2024.11.28 |