공통 컴포넌트를 작성하는중에, children 으로 받아온 자식요소에 대해서 index 를 주어야 할 일이 생겼다. 물론 사용하려고 불러오는 컴포넌트에서 다음처럼 index 값이나 id 를 개별적으로 넘겨주어도 되지만 map을 돌리지 않는 이상, 너무 귀찮은 일이라고 생각이 들었다.
그리고 나는 context로 accordion 컴포넌트에서 전역적으로 데이터를 관리하므로, item에서 받아온 정보를 또 props 로 넘겨주거나 개별 컴포넌트마다 id 를 뿌려주는 귀찮은 짓을 해야해서 사용자가 단순히 children 만 입력해도 기능이 동작하게 끔 최대한 기본컴포넌트에서 기능을 구현하고 싶었다.
<Accordion>
<AccordionItem id=1>
<AccordionTrigger>open1</AccordionTrigger>
<AccordionContent>
content1
</AccordionContent>
</AccordionItem>
<AccordionItem id=2>
<AccordionTrigger>open2</AccordionTrigger>
<AccordionContent>
content2
</AccordionContent>
</AccordionItem>
<AccordionItem id=3>
<AccordionTrigger>open3</AccordionTrigger>
<AccordionContent>
content3
</AccordionContent>
</AccordionItem>
</Accordion>
그래서 찾아본 React.Children.map() 함수. 저렇게 위처럼 데이터를 전달해도 다음처럼 children 을 인식해서 map을 돌려 그 안에 해당하는 자식요소의 속성을 수정할 수 있다.
child 가 validElement라면 각 child에 { index: index } 값을 전달하므로, 각자 고유의 index 값을 갖게 되는 것이다.
<AccordionContext.Provider value={{ activeIndex, handleToggleAccordion }}>
<div {...props}>
{React.Children.map(children, (child, index) => {
if (React.isValidElement(child)) {
return cloneElement(child as ReactElement, { index });
}
return child;
})}
</div>
</AccordionContext.Provider>
https://legacy.reactjs.org/docs/react-api.html#reactchildrenmap
따라서 Accordion 컴포넌트의 자식 컴포넌트인 AccordionItems 에는 다음과 같이 부모로부터 index 를 받아와서 공유할 수 있다.
const AccordionItem = ({ children, index, ...props }: AccordionItemProps) => {
return (
<div {...props} className={styles.accordionItem} data-index={index}>
{React.Children.map(children, (child) => {
if (React.isValidElement(child)) {
return cloneElement(child as ReactElement, { index });
}
return child;
})}
</div>
);
};
이에 따라서 다음과 같이 조건문을 걸어주면, 해당 index 일 경우 한번 더 클릭하면 아코디언이 닫히고, 다른 index일 경우 트리거 된다.
const handleToggleAccordion = (index: number) => {
setActiveIndex((prevIndex) => (prevIndex === index ? null : index));
};
혹시몰라서 작성해본 객체배열에 map을 돌려서 사용해도 아주 잘 동작한다.
const list = [
{
trigger: "트리거1",
content: "내용1",
},
{
trigger: "트리거2",
content: "내용2",
},
];
<Accordion>
{list.map((v, i) => (
<AccordionItem key={i}>
<AccordionTrigger>{v.trigger}</AccordionTrigger>
<AccordionContent>{v.content}</AccordionContent>
</AccordionItem>
))}
</Accordion>
'TIL' 카테고리의 다른 글
TIL 24.11.29 React svg component 불러오기 (0) | 2024.12.02 |
---|---|
TIL 24.11.14 Prisma 전역 객체 선언 및 타입 지정 (0) | 2024.11.14 |
TIL 24.11.06 className, style 등 기본 props type 지정 (1) | 2024.11.06 |
TIL 24.10.28 react-draggable positionOffset, bounds 위치 문제 해결 (1) | 2024.10.28 |
TIL 24.08.28 Another git process seems to be running in this repository 에러 (0) | 2024.08.28 |