ReactNode vs JSX.Element vs ReactElement

김상근·2024년 2월 27일
0

📖 Introduction

타입스크립트와 함께 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의 리턴 타입이 바로 ReactElementJSX.Element이다.

ReactNode vs JSX.Element vs ReactElement


세 타입의 포함 관계이다.

1. ReactElement

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

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

2. 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;

**ReactNodeReactElement의 superset**입니다. ReactNode는 ReactElement일 수 있고 ReactFragment, string, number, ReactNode의 Array, null, undefined, boolean 등의 좀 더 유연한 타입 정의라고 할 수 있다.

3. JSX.Element

declare global {
  namespace JSX {
    interface Element extends React.ReactElement<any, any> {}
  }
}

JSX.ElementReactElement의 특정 타입이라고 생각하면 된다. JSX.Elementpropstypeany인 제네릭 타입을 가진 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

profile
성장해 나가는 프론트엔드 개발자가 되기 위해 노력하는 중입니다. 적극적인 피드백을 환영합니다.

0개의 댓글