일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- docker
- 파이썬
- es6
- 솔리디티
- 컴퓨터공학
- 프론트엔드
- react
- 알고리즘
- 클라우드
- BFS
- AWS
- 쿠버네티스
- node.js
- 이슈
- 리액트
- HTML
- 이더리움
- 백엔드
- 가상화
- 백준
- CSS
- kubernetes
- 자바스크립트
- k8s
- 타입스크립트
- 웹
- JavaScript
- next.js
- 블록체인
- TypeScript
- Today
- Total
즐겁게, 코드
ReactNode, ReactChild, ReactElement 타입 비교 본문
자식 요소를 감싸는 래퍼 컴포넌트를 작성할 때 자식 요소인 children
속성의 타입을 명시해야 하는 경우가 자주 있습니다.
그런데, ReactChild
, ReactElement
, ReactNode
등 비슷한 이름을 갖는 타입이 너무 많은 모습이네요!
오늘은 자주 사용되는 타입 설명과 함께 어떤 상황에서 각 타입을 사용할 수 있을지 간단히 알아보도록 하겠습니다.
const App = () => {
return (
<div className="App">
<Wrapper>
{/* 과연 children 요소의 타입은 무엇일까요? */}
<div>Hello, world!</div>
</Wrapper>
</div>
);
};
type WrapperProp = {
// children: React.ReactChild;
// children: React.ReactElement;
// children: JSX.Element;
// children: React.ReactChildren;
// children: React.ReactNode;
// ...으아아아!! ...
};
const Wrapper = ({ children }: WrapperProp) => {
return (
<div className="Wrapper">
<div>{children}</div>
</div>
);
};
export default App;
React.ReactNode
제일 처음 알아볼 타입은 ReactNode
타입입니다.ReactNode
는 children
속성의 타입으로 가장 많이 사용하는 타입이기도 한데요, 과연 어떤 특징을 갖고 있을까요?
// ReactChild 타입에 string, number 타입이 포함되어 있습니다.
type ReactNode = ReactChild | ReactFragment | ReactPortal | boolean | null | undefined;
ReactNode
타입은 jsx
내에서 사용할 수 있는 모든 요소의 타입을 의미하는데요, 즉 string
, null
, undefined
등을 포함하는 가장 넓은 범위를 갖는 타입입니다.
TMI : ReactNode 타입은 클래스 컴포넌트 의 render 함수가 기본적으로 리턴하는 타입이기도 합니다!
반면 함수 컴포넌트는 ReactElement 인터페이스를 리턴합니다!
React.ReactElement
두 번째로 알아볼 타입은 ReactElement
타입입니다.
VSCode에서 ReactElement
를 확인해보면 아래처럼 생긴 것을 확인할 수 있는데요, 이게 도대체 무슨 뜻일까요?
interface ReactElement<P = any, T extends string | JSXElementConstructor<any> = string | JSXElementConstructor<any>> {
type: T;
props: P;
key: Key | null;
}
이 아리송한 코드만 봐서는 뭔지 알기 어려우니, 답을 먼저 소개하자면 ReactElement
는 createElement
함수를 통해 생성된 객체의 타입입니다.
또한 ReactNode
와는 달리 원시 타입을 허용하지 않고 완성된 jsx
요소만을 허용하는데요, 즉 위 사진에서 확인할 수 있는 것처럼 ReactNode
타입이 ReactElement
타입을 포함하고 있는 관계임을 확인할 수 있습니다.
따라서 위 사진처럼 원시 타입 리터럴을 children
속성으로 사용하려 하면 에러를 출력하는 모습인데요, 따라서 ReactElement
타입은 자식 요소로 하나의 "컴포넌트" 를 받는 것을 강제해야 하는 상황에 사용할 수 있습니다.
createElement 함수의 리턴값
시간이 조금 있으니, 한번 createElement와 ReactElement의 관계도 확인해볼까요?jsx
로 간단한 태그를 만들어본 다음 바벨을 통해 트랜스파일을 수행해 보겠습니다.
<div name="chanmin">Hello, {name}</div>
그 결과, 아래와 같은 결과물이 출력됩니다.
"use strict";
/*#__PURE__*/
React.createElement("div", {
name: "chanmin"
}, "Hello, ", name);
트랜스파일된 결과물(createElement
함수)을 확인해보면 생성한 태그에 대한 정보가 담겨 있는데요, 자세히 살펴보면 ReactElement
가 가진 type
과 prop
이 담겨있는 모습입니다.
따라서, createElement
함수가 리턴한 모든 객체는 ReactElement
인터페이스를 구현하고 있음을 알 수 있습니다.
그럼 혹시 JSX.Element
와 ReactElement
의 차이는 무엇인가요?
거의 없습니다!
// index.d.ts
declare global {
namespace JSX {
interface Element extends React.ReactElement<any, any> { }
...
}
}
JSX.Element
는 단순히 ReactElement
인터페이스를 상속받은 인터페이스인데요, 내부 구조나 제약 타입이 별도로 존재하지 않아 완전히 동일하다고 봐도 무방합니다.
React.ReactChild
마지막으로 알아볼 타입은 ReactChild
타입입니다.
type ReactChild = ReactElement | ReactText;
ReactChild
는 ReactNode
타입을 적절히 내로잉(narrowing)한 타입인데요, ReactElement
타입이 리액트 요소 객체만을 허용했다면, ReactChild
타입은 여기서 원시 타입까지는 허용하는 타입입니다.
TL;DR;
타입별 허용 범위 비교 : ReactNode
> ReactChild
> ReactElement
ReactNode
타입은 컴포넌트, 원시 타입,null
,undefined
를 모두 허용하는 타입입니다.
type ReactNode = ReactChild | ReactFragment | ReactPortal | boolean | null | undefined;
ReactChild
타입은 컴포넌트, 원시 타입 리터럴을 허용하는 타입입니다.
type ReactChild = ReactElement | ReactText;
ReactElement
타입은createElement
함수를 통해 생성된 컴포넌트만을 허용하는 타입입니다.
interface ReactElement<P = any, T extends string | JSXElementConstructor<any> = string | JSXElementConstructor<any>> {
type: T;
props: P;
key: Key | null;
}
'🎨 프론트엔드 > React.js' 카테고리의 다른 글
이벤트 핸들러, 제대로 제거하고 계셨나요? (1) | 2021.12.31 |
---|---|
미리 만나보는 automatic batching (0) | 2021.12.27 |
야, 너도 상태관리 할 수 있어 2편 : 리코일 사용하기 (0) | 2021.08.25 |
야, 너도 상태관리 할 수 있어 1편 : 리코일이란? (1) | 2021.08.24 |
클라이언트 인증 구현하기 : 인증이란 무엇인가? (0) | 2021.08.21 |