티스토리 뷰
타입스크립트로 리액트 코드를 작성하다보면 컴포넌트의 타입을 정해줘야하는데 너무 여러가지 타입이 있어서 헷갈려서 정리하고자 합니다.
배경지식
클래스형 컴포넌트는 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의 리턴 타입이 바로 ReactElement와 JSX.Element이다.
포함관계
ReactElement와 JSX.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 등등.. 좀 더 유연한 타입 정의라고 할 수 있다.
JSX.Element
JSX.Element는 ReactElement의 특정 타입이라고 생각하자. 또한 글로벌 네임스페이스에 정의되어 있어 외부 라이브러리에서 이 JSX.Element를 재정의할 수 있다.
declare global {
namespace JSX {
interface Element extends React.ReactElement<any, any> { }
}
}
예시
<p> // <- ReactElement = JSX.Element
<Custom> // <- ReactElement = JSX.Element
{true && "test"} // <- ReactNode
</Custom>
</p>
질문
왜 클래스형 컴포넌트의 render()는 ReactNode를 리턴하고 함수형 컴포넌트는 ReactElement를 리턴하는가?
historical reasons이라고 한다.
ReactNode이외에는 null타입을 가지지 않으므로 ReactElement를 리턴하는 함수형 컴포넌트에서는 아래와 같이 null을 union해줘야한다.
const example = (): ReactElement | null => {
if(/* true조건 */) return null;
return <p>Hello World</p>;
};
'React' 카테고리의 다른 글
React-virtualized의 AutoSizer (0) | 2020.06.15 |
---|---|
JSX element type 'TabComponent' does not have any construct or call signatures. (0) | 2020.06.03 |
당신의 프론트엔드를 리액트로 이전하는 방법 (0) | 2020.05.15 |
useRef vs document.getElementById 둘중 어느게 더 좋을까? (0) | 2020.05.07 |
emotion 사용시 jsx pragma 제거하기 (0) | 2020.04.17 |
- Total
- Today
- Yesterday
- computed
- type alias
- webpack
- reducer
- Babel
- return type
- Polyfill
- await
- Action
- useRef
- server side rendering
- state
- mobx
- useEffect
- Next.js
- es6
- react hooks
- design system
- props
- async
- react
- hydrate
- javascript
- promise
- rendering scope
- atomic design
- typescript
- storybook
- reflow
- reactdom
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |