리액트에서 radio input을 컴포넌트화하는 과정에서 자식들에게 동일한 props를 내려주는 방법이 있지 않을까하는 생각이 들었다.
input의 name 속성이 같은 묶음끼리 함께 작동하는데 이것을 하나하나 넣어주는 과정이 똑똑하지 못하다고 생각했다.
이런 형태의 radio input이 있다고 생각해보자
const Radio = ({ id, defaultChecked = false, children, name }: Props) => {
return (
<Label id={id}>
<Input type="radio" id={id} name={name} defaultChecked={defaultChecked} />
<LabelName>{children}</LabelName>
</Label>
);
};
이것을 하나의 묶음으로 불러오려면
<RadioGroup>
<Radio id="apple" name="user">사과</Radio>
<Radio id="banana" name="user">바나나</Radio>
<Radio id="mango" name="user">망고</Radio>
</RadioGroup>
이러한 형태가 될것이다.
RadioGroup에 name 속성을 주고 아래에 뿌려준다면 훨씬 코드가 간결해질 것이다.
const RadioGroup = ({ children, name }: Props) => {
const renderChildren = () => {
return React.Children.map(children, (child) => {
return React.cloneElement(child, {
name,
});
});
};
return <Container>{renderChildren()}</Container>;
};
React에는 cloneElement라는 것이 있다.
기존 element의 클론을 만드는것인데 여기에 새로운 속성을 추가로 넣어줄 수 있는 모양이다.
React.Children.map을 이용해 들어온 children을 하나하나 대입하고
그에 맞는 name prop을 추가해준 복제품을 렌더링하는 방식이다.
위의 renderChildren() 함수를 사용하면 RadioGroup의 하위 컴포넌트들에 name 속성을 넣어줄 수 있게 된다.
<RadioGroup name="user">
<Radio id="apple">사과</Radio>
<Radio id="banana">바나나</Radio>
<Radio id="mango">망고</Radio>
</RadioGroup>
해당 방법으로 하위 컴포넌트에 속성을 부여해줄 경우 TypeScript에서는 몇가지 에러가 호출되었다.
typescript 에서는 cloneElement 함수의 파라미터로 넣을 children의 type을 React.ReactElement로 지정해야만 한다. ReactNode를 주로 사용하는데 ReactNode를 사용할경우 type error가 호출되었다.
하지만 React.ReactElement로 지정할 경우 children은 하나의 element만 가질 수 있다는 오류가 난다. 그래서 React.ReactElement | React.ReactElement[] 라고 배열도 허용해주어야 여러개의 컴포넌트를 넣을 수 있었다.
상위에서 뿌려준 name 속성이 하위 자식에게 제대로 들어가기 위해서는 자식 컴포넌트의 type에 name을 정의해 주어야한다. 그러나 name은 부모컴포넌트에서 가져오기 때문에 자식 컴포넌트에는 작성하지 않는다(그것을 위해 하는 작업이기때문에). 그러나 react에서는 name속성을 명시하라는 오류가 나오게 된다.
이를 해결하기 위해 자식 컴포넌트의 name type에 undefined를 추가하면 된다. 이것이 맞는 방법인지는 모르겠으나 일단 빨간줄도 사라지고 작동도 잘 된다.
이러한 방식을 뭐라고 부르는지 용어를 몰라서 조금 버벅였으나 그렇게 어렵지않게 찾을 수 있었다.
그러나 국내 문서가 없었기 때문에 조금 보완하여 함께 작성하게 되었다.
출처: https://sentry.io/answers/react-passing-props-to-children/