무엇
Children은 컴포넌트가 children prop으로 받은 JSX를 순회하고 변형하는 API다. map, forEach, count, toArray, only 다섯 개 함수로 구성된다.
React 공식 문서는 이 API를 "드물게 쓰이고 취약한 코드로 이어지기 쉽다"고 못박는다. 레거시 취급이다.
import { Children } from 'react'
Children.count(children)
Children.map(children, (child) => child)
무슨 일이 일어나나
children은 컴포넌트가 JSX로 받은 노드 구조 그대로다. 이때 Children의 순회는 한 겹만 본다 — 하위 컴포넌트가 실제로 렌더링한 결과까지는 내려가지 않는다.
<RowList>
<p>첫 항목</p>
<MoreRows />
</RowList>
RowList가 받는 children은 <p>와 <MoreRows /> 딱 두 개다. MoreRows가 실제로 몇 개를 렌더링하든 Children은 그 내부를 모른다. Children.count(children)은 항상 2다.
빈 노드(null, undefined, boolean), 문자열, 숫자, 엘리먼트는 각각 하나의 노드로 센다. 배열 자체는 노드로 안 치고 그 안의 항목만 센다. Fragment는 순회하지 않는다.
사용법
Children.map이 가장 자주 쓰인다. 반환값의 key는 원래 항목의 key와 자동으로 합쳐진다.
import { Children } from 'react'
function RowList({ children }: { children: React.ReactNode }) {
return (
<div className="RowList">
{Children.map(children, (child) => (
<div className="Row">{child}</div>
))}
</div>
)
}
Children.only는 children이 엘리먼트 하나뿐임을 강제한다. 배열이면(항목이 하나뿐이어도) 에러를 던진다.
Children.only(children) // 엘리먼트 하나가 아니면 throw
실무 예시
레거시 API라 실무에서 새로 쓸 일은 거의 없다. 공식 문서가 권하는 대안 쪽이 실제로 쓰는 패턴이다 — 명시적 하위 컴포넌트를 노출하거나(<Row>), 배열을 prop으로 받거나(rows={[...]}), render prop을 쓴다.
// Children.map 대신 — 컴포넌트를 더 쪼개도 계속 동작한다
function Row({ children }: { children: React.ReactNode }) {
return <div className="Row">{children}</div>
}
왜 중요한가
공식 문서 스스로 우회로를 권한다. 새 코드에서 이 API로 시작할 이유가 없고, 기존 클래스형 컴포넌트나 오래된 라이브러리 코드를 읽을 때 알아보는 용도에 가깝다. 존재와 함정만 알면 되는 레거시다.
Reference