Render Delegation
ref- https://kciter.so/posts/render-delegation-react-component/#user-content-fn-1
- 속성과 행동들을 자식 컴포넌트에게 넘긴 후, 자식이 직접 부모 컴포넌트를 대신하여 렌더링하는 방법
- Radix 에서 사용하여 유명해진 방법으로, 보통
asChild
라는 속성을 통해 해당 기능을 사용 할 수 있다.
예시
<Label.Root>
라벨
</Loble.Root>
- 위의 코드는
<label> 라벨 <label/>
로 렌더링 된다.
<Label.Root asChild>
<a ref='something'>라벨</a>
</Label.Root>
- 위의 코드는
<a>라벨</a>
로 렌더링 된다. - 즉 부모 (label)이 아닌, 자식(a)이 대신 렌더링된다.
내부 구현 : Slot & Slottable
- Slot 은 자식 컴포넌트를 렌더링하는 역할이고
- Slottable 은 Slot 에 들어갈 것이 무엇인지 나타낸다.
- Radix 에서는 요 Slot 과 Slottable 을 제공하여, 사용자가 직접 Redner Delegation 컴포넌트를 만들 수 있게 해준다.
- radix 원본코드 보기
Slottable 컴포넌트
const Slottable = ({children}) => {
return <>{children}</>
}
- 기본으로 children 을 받아와서 바로 꽂아주는 것 밖에 없다.
Slot 컴포넌트
conset Slot = ({children, ...props}) => {
const childrenArray = React.children.toArray(children);
const slottable = childrenArray.find((child)=> {
return React.isValidElement(child) & child.type === Slottable;
})
if(slottable){
// slottable 이 있다면, slottable 의 자식을 새로운 요소(newElement)로 사용
const newElement = slottable.props.children;
// 새로운 자식 요소 생성
const newChildren = childrenArray.map((child)=> {
// Slottable 이 아니라면 그대로 반환하고
if(child !== slottable) return child;
// Slottable 이라면 해당 영역을 자식 컴포넌트의 children 으로 교체한다.
if(React.isValidElement(newElement)){
return newElement.props.children;
}
})
if(React.isValidElement(newElement)){
return React.cloneElement(
newElement,
{...props, ...newElement.props},
newChildren
)
}
return null;
}
if(React.isValidElement(children)){
return React.cloneElement(children, {
...props,
...children.props,
})
}
return null;
}
- children 으로 받은 JSX 요소를 렌더링하되, children 이 올바른 JSX 요소라면 props 를 합서아여 새로운 컴포넌트를 만들어 렌더링한다.
- 즉, Slottable 요소를 찾아내어, 그 내용을 새로운 루트 요소로 사용한다.
- 원래의 자식 요소들을 이 새로운 루트 요소의 자식으로 삽입한다.
- 이를 통해 부모의 렌더링을 자식 컴포넌트가 대신하고, 부모의 속성/행동을 자식에게 전달함.
실제로 컴포넌트에 적용한다면 아래와 같이 된다.
// Button 컴포넌트
const Button = ({ children, ...props }) => {
return (
<Slot {...props}>
<button className="ds-button">
<Slottable>{children}</Slottable>
</button>
</Slot>
);
};
// 사용 예시
<Button asChild>
<a href="/some-link">Click me</a>
</Button>
언제쓸 수 있을까?
- 현재 사내 디자인시스템 컴포넌트에서는 해당 부분을
as prop
으로 처리해주고 있다. 예를들어<Button as='a'/>
이런식으로..- 장점은, 사용 명세가 간단하다는 것이며 동작이 제한적이어서 예측가능하다는 것이다.
- 단점은, (장점과 이어짐) 동작이 제한적이라는 것이다.
- 그러면 Redner Delegation 방식은..
- 장점은, 재사용이 매우 편리하다는 것이고, 런타임의 컴포넌트 내부구조에 대한 다양한 연산이 가능하다는 것인데
- 단점은, (장점과 이어짐) 런타임에 다양한 연산이 가능하다는 것은 그만큼 복잡도가 높다는 것이고, 사용 명세가 까다롭다는 것이다.
- 따라서 내 생각에는 디자인시스템 내에서 왠만한 경우는 as prop으로 대응가능하다고 생각하고,
- 그 외의 런타임 시 마크업 구조 등의 변경이 필요한 케이스에서만 Render Delegation 을 적용해보는게 좋다고 생각한다.
- 그런데 생각보다 런타임 시 변경이 필요한 케이스는 많지 않을 것 같다.