REACT

cloneElement

gurwhddl 2023. 7. 25. 15:00

cloneElement란

[https://react.dev/reference/react/cloneElement]

리액트 최상위 API로 말 그대로 Element를 복제(clone)해서 반환해주는 역할을 함

리액트로 sns 사이트를 만들고 있는데 form을 사용해야 하는 경우가 매우 많음
ex)회원가입 , 로그인 , 프로필 변경 등등...
각 form에서 수집하는 데이터 또한 다르고, form 끼리 중복되지 않는 경우도 있음

  • 회원가입의 경우 아이디,비밀번호,닉네임,폰번호,집주소....
  • 프로필 변경의 경우에는 프로필사진,닉네임...

그래서 일단 Form과 Input 컴포넌트를 만들어놓고, 필요한 페이지에 따라 Input을 추가하려고 했는데

react-hooks-form을 사용하고 있었기 때문에, Form 컴포넌트에서 생성한 register라는 함수를 자식인 Input에 전달해줘야 했는데
그냥 {children}을 쓰게 되면 Input 컴포넌트는 렌더링 되겠지만, register라는 함수를 전달해줄 수가 없음

      <form
        onSubmit={handleSubmit((data) => {
          submitEventHandler(data);
        })}>
        {Children.map(children, (child) =>
          cloneElement(child, {
            htmlFor: child.props.type,
            register: checkType(register, child.props.type),
            error: errors[child.props.type],
          })
        )}

        <Button type="submit">제출</Button>
      </form>
  • 요약하자면 자식으로 오는 컴포넌트에 특정 props를 전달해서 쓰고 싶다는 소리

  • 부모 컴포넌트에서 자식 컴포넌트에 props를 '동적'으로 넣어야 할 때 사용하면 좋음

  • Input 컴포넌트의 props를 해당 Input의 기존 props 값에 따라 동적으로 할당해줄 수 있음

  • 자식 컴포넌트가 여러개 일때 Children.map을 사용하지 않으면 오류 발생

또 다른 방법

공식 문서에서
Using cloneElement is uncommon and can lead to fragile code 라고 소개하면서 제공한 대안은

export default function List({ items, renderItem }) {  
const \[selectedIndex, setSelectedIndex\] = useState(0);  
return (
{items.map((item, index) => {  
const isHighlighted = index === selectedIndex;  
return renderItem(item, isHighlighted);  
})}
<List  
items={products}  
renderItem={(product, isHighlighted) =>  
  <Component props...>
}  
/>
  • cloneElement에서는 추가하고 싶은 컴포넌트를 자식 태그에 추가했다면, 이 방법은 props를 통해서 전달
  • items에는 전달하고 싶은 props의 데이터를 , renderItem은 컴포넌트와 props를 지정해놓고 map을 통해서 return