컴포넌트 합성(composition)과 props.children
📄 리액트 공식 문서 Composition vs Inheritance
1. props.children
리액트 공식 문서에서는 props.children에 대해 다음과 같이 설명한다.
“어떤 컴포넌트들은 어떤 자식 엘리먼트가 들어올 지 미리 예상할 수 없는 경우가 있다. 범용적인 ‘박스’ 역할을 하는 Sidebar 혹은 Dialog와 같은 컴포넌트에서 특히 자주 볼 수 있다.”
나는 props.children을 사용하는 것이, 컴포넌트에 구멍을 뚫어주는 것이라 해석했다. 어떤 자식 컴포넌트가 들어올지 알 수 없는 경우에 {props.children}
으로 구멍을 뚫어 자리를 마련해두고, 그 자리에 임의의 자식 컴포넌트가 들어올 수 있도록 하는 것이다.
function FancyBorder(props) {
return (
<div className={"FancyBorder FancyBorder-" + props.color}>
{props.children}
</div>
);
}
다음과 같이 다른 컴포넌트에서 props.children이라는 구멍에 JSX를 중첩해서 임의의 자식을 전달할 수 있다.
function WelcomeDialog() {
return (
<FancyBorder color="blue">
{/* 여기부터 */}
<h1 className="Dialog-title">Welcome</h1>
<p className="Dialog-message">Thank you for visiting our spacecraft!</p>
{/* 여기까지 */}
</FancyBorder>
);
}
만약 컴포넌트에 두 개 이상의 구멍이 필요하다면, children 대신 자신만의 방식으로 적용해볼 수도 있다.
function SplitPane(props) {
return (
<div className="SplitPane">
<div className="SplitPane-left">{props.left}</div>
<div className="SplitPane-right">{props.right}</div>
</div>
);
}
function App() {
return <SplitPane left={<Contacts />} right={<Chat />} />;
}
<Contacts />
와 <Chat />
같은 React 엘리먼트는 단지 (함수) 객체이기 때문에 다른 데이터처럼 prop으로 전달할 수 있다. React에서 prop으로 전달할 수 있는 것에는 제한이 없다.
따라서 태그를 동적으로 처리해야하는 경우에도 props.children을 사용할 수 있다.
const li_Array = ["First item.", "Second item."];
const Category = (props) => {
return <ul>{props.children}</ul>;
};
const App = () => (
<Category>
{li_Array.map((value, idx) => (
<li key={idx}>{value}</li>
))}
</Category>
);
3. props.children 사용해보기
레이아웃 컴포넌트를 만들어서 _app.tsx 혹은 _app.js에서 Component를 감싸주어 페이지가 변경되어도 모든 페이지에서 레이아웃이 공통적으로 보여지고, 레이아웃 컴포넌트 내부의 Component, 즉 props.children만 변경되도록 할 수 있다.
Layout 컴포넌트
export default function Layout(props: ILayoutProps) {
return (
<>
<LayoutHeader />
<LayoutBanner />
<LayoutNavigation />
<LayoutSidebar />
<Body>{props.children}</Body>
<LayoutFooter />
</>
);
}
App 컴포넌트
const App = ({ Component, pageProps }: AppProps) => {
return (
<RecoilRoot>
<ApolloSetting>
<Global styles={globalStyles} />
<Layout>
<Component {...pageProps} />
</Layout>
</ApolloSetting>
</RecoilRoot>
);
};
export default App;
댓글남기기