[TypeScript] ReactNode와 ReactElement

고병표·2022년 3월 14일
0

TypeScript

목록 보기
5/5

ReactNode와 ReactElement 차이점?

클래스형 컴포넌트는 render메소드에서 ReactNode를 리턴한다. 함수형 컴포넌트는 ReactElement를 리턴한다.

JSX는 바벨에 의해서 React.createElement(component, props, ...children) 함수로 트랜스파일된다.

html 처럼 생긴 문법을 리액트 라이브러리의 렌더링 함수로 변환하는것이다. 그래서 JSX를 사용하지 않고도 리액트를 사용할 수 있으나(JSX없이 사용하는 React) 이렇게 하면 매우 불편하다.

// jsx
<div>Hello {this.props.toWhat}</div>
<Hello toWhat="World" />

JSX는 아래 처럼 변경된다.

// transpile
React.createElement('div', null, `Hello ${this.props.toWhat}`);
React.createElement(Hello, {toWhat: 'World'}, null);

이 React.createElement의 리턴 타입이 바로 ReactElementJSX.Element이다.

포함관계

ReactElementJSX.Element는 묶어서 생각해도 좋다. JSX.Element의 제네릭 인터페이스가 ReactElement이다.

ReactElement

interface ReactElement<P = any, T extends string | JSXElementConstructor<any> = string | JSXElementConstructor<any>> {
    type: T;
    props: P;
    key: Key | null;
}

React.createElement를 호출하면 이런 타입의 객체가 리턴된다. 그냥 리액트 컴포넌트를 JSON 형태로 표현해놨다고 생각하자.

ReactNode

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일 수 있고 null, undefined, boolean 등등.. 좀 더 유연한 타입 정의라고 할 수 있다.

질문

왜 클래스형 컴포넌트의 render()는 ReactNode를 리턴하고 함수형 컴포넌트는 ReactElement를 리턴하는가?

historical reasons이라고 한다.

ReactNode이외에는 null타입을 가지지 않으므로 ReactElement를 리턴하는 함수형 컴포넌트에서는 아래와 같이 null을 union해줘야한다.

const example = (): ReactElement | null => {
	if(/* true조건 */) return null;
    
	return <p>Hello World</p>;
};

0개의 댓글