타입스크립트와 함께 React를 사용하다보면 children을 Props로 넘겨줘야하는 경우가 자주 있다. 기존에 사용해오던 타입은 ReactNode Type이었는데 면접 중 ReactNode와 ReactElement의 차이를 알고있냐는 질문에 아무 대답도 하지 못했다. 그래서 이 둘의 차이에 대해서 정리하기 위해서 글을 작성하게 되었다.
클래스형 컴포넌트는 render메소드에서 ReactNode를 리턴하고, 함수형 컴포넌트는 ReactElement를 리턴한다. 이후 JSX는 바벨에 의해서 React.createElement(components, props, ...children) 함수로 트랜스파일된다.
즉, html 처럼 생긴 문법을 리액트 라이브러리의 렌더링 함수로 변환하는 것이다. 그래서 JSX를 사용하지 않고도 리액트를 사용할 수 있지만 이 경우 일일히 함수를 이용해야하므로 매우 불편하다.
// jsx
<div>Hello {this.props.toWhat}</div>
<Hello toWhat="World" />
// transpile
React.createElement('div', null, `Hello ${this.props.toWhat}`);
React.createElement(Hello, {toWhat: 'World}, null);
React.createElement의 리턴 타입이 바로 ReactElement와 JSX.Element이다.

세 타입의 포함 관계이다.
interface ReactElement<P = any, T extends string | JSXElementConstructor<any> = string | JSXElementConstructor<any>> {
type: T;
props: P;
key: Key | null;
}
React.createElement를 호출하면 이런 타입의 객체가 리턴된다. 단순하게 리액트 컴포넌트를 JSON 형태로 표현해놨다고 생각하면 된다. ReactElement는 type과 props를 가진 객체이다.
type ReactText = string | number;
type ReactChild = ReactElement | ReactText;
interface ReactNodeArray extends Array<ReactNode> {}
type ReactFragment = {} | ReactNodeArray;
type ReactNode = ReactChild | ReactFragment | ReactPortal | boolean | null | undefined;
**ReactNode는 ReactElement의 superset**입니다. ReactNode는 ReactElement일 수 있고 ReactFragment, string, number, ReactNode의 Array, null, undefined, boolean 등의 좀 더 유연한 타입 정의라고 할 수 있다.
declare global {
namespace JSX {
interface Element extends React.ReactElement<any, any> {}
}
}
JSX.Element는 ReactElement의 특정 타입이라고 생각하면 된다. JSX.Element는 props와 type이 any인 제네릭 타입을 가진 ReactElement입니다. JSX는 글로벌 네임 스페이스에 있기 때문에 다양한 라이브러리에서 자체 라이브러리의 방법대로 실행될 수 있습니다.
<p> // ReactElement = JSX.Element
<Custom> // ReactElement = JSX.Element
{true && 'test'} // ReactNode
</Custom>
</p>
ReactNode 이외에는 null 타입을 가지지 않으므로 ReactElement를 리턴하는 함수형 컴포넌트에서는 아래와 같이 null을 union 해줘야 한다.
const example = (): ReactElement | null => {
if(/* true조건 */) return null;
return <p>Hello World</p>;
};
참고 래퍼런스
https://velog.io/@hanei100/ReactElement-vs-ReactNode-vs-JSX.Element