본문 바로가기
React

ReactNode와 ReactElement의 차이

by ttum 2024. 2. 20.

props로 컴포넌트(children)를 넘기거나 하는 상황에서 엘리먼트의 타입을 지정해주는데, 이때 ReactNode와 ReactElement의 차이가 헷갈려 찾아보게 되었다. 이런 경우에는 어떤 타입이 맞을까?

export interface IEditorLayoutProps extends WithTranslation {
  children: React.ReactElement;
}

export interface IEditorLayoutProps extends WithTranslation {
  children: React.ReactNode;
}

 

결론부터 말하자면 둘다 맞다. 왜냐하면 ReactNode가 ReactElement의 슈퍼셋이기 때문이다. 아래와 같은 관계이다.

리액트에 정의된 타입을 봐도 확인할 수 있다. ReactElement는 ReactNode의 유니언타입 중 하나이다.

export type ReactNode =
  | React$Element<any>
  | ReactPortal
  | ReactText
  | ReactFragment
  | ReactProvider<any>
  | ReactConsumer<any>;

export type ReactEmpty = null | void | boolean;

export type ReactFragment = ReactEmpty | Iterable<React$Node>;

export type ReactNodeList = ReactEmpty | React$Node;

export type ReactText = string | number;

export type ReactProvider<T> = {
  $$typeof: symbol | number,
  type: ReactContext<T>,
  key: null | string,
  ref: null,
  props: {
    value: T,
    children?: ReactNodeList,
  },
};

 

ReactNode

ReactNode는 ReactElement를 포함하여 string, number, boolean 등의 값들도 가질 수있다. ReactNode가 falsy한 값을 리턴하면  아무것도 렌더링을 하지 않는다. 리액트를 render해주는 함수의 파라미터 값이 ReactNode인데, 여기에 ReactNode가 받는 다양한 값들을 넣어보면 이해하기 쉽다. 이렇게 ReactNode 타입을 파라미터로 가지는 render함수에 다양한 값들을 넣어보면 해당 데이터가 렌더링 되는 모습을 볼 수 있다. ReactElement는 ReactNode가 받을 수 있는 타입 중 일부인 것이다.

// ReactElement 타입을 넣은 경우.
ReactDOM.createRoot( 
  document.querySelector('#root')
).render(<App/>)

// string 타입을 넣은 경우.
ReactDOM.createRoot( 
  document.querySelector('#root')
).render('Hello world')

// number 타입을 넣은 경우.
ReactDOM.createRoot( 
  document.querySelector('#root')
).render(100)

// boolean 타입을 넣은 경우.
ReactDOM.createRoot( 
  document.querySelector('#root')
).render(false)

// null 타입을 넣은 경우.
ReactDOM.createRoot( 
  document.querySelector('#root')
).render(null)

ReactElement

ReactElement의 타입은 아래와 같이 정의되어 있다. ReactElement는 createElement의 결과물의 타입이다.

export type ReactElement = {
  $$typeof: any,
  type: any,
  key: any,
  ref: any,
  props: any,
  // ReactFiber
  _owner: any,

  // __DEV__
  _store: {validated: boolean, ...},
};

 

`<Greeting name="Taylor" />`와 `createElement(Greeting, {name: 'Taylor'})`은 아래와 같은 동일한 결과물을 만들어낸다.

// Slightly simplified
{
  type: Greeting,
  props: {
    name: 'Taylor'
  },
  key: null,
  ref: null,
}

 

선언되고 호출이 된 컴포넌트의 결과물이 ReactElement인 것이다. 

 

정리

  • 컴포넌트에서 반환되는 값이 ReactNode이며, 여기에서는 ReactElement를 포함하여 number, string, null, boolean 등 다양한 값이 들어갈 수 있다.
  • JSX를 반환하는 컴포넌트는 내부적으로 React.createElement 함수를 호출해 ReactElement 타입의 결과물을 생성한다. 이 ReactElement는 컴포넌트의 타입(예: HTML 태그 이름 또는 사용자 정의 컴포넌트), props, key(리스트 내 항목의 고유 식별자), 그리고 ref 등의 정보를 포함한다.

 

출처

- [react.dev] What is a ReactElement, exactly?