타입스크립트와 함께 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