StudyForYou / ouahhan-typescript-with-react

우아한 타입스크립트 with 리액트 스터디 레포 🧵
4 stars 0 forks source link

#21 [8장_1] ReactElement, ReactNode, JSX.Element에 대해서 간단하게 설명해주세요! #33

Closed hyeyoonS closed 2 months ago

hyeyoonS commented 2 months ago

❓문제

🎯답변

ReactElement

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

ReactNode

type ReactNode =
  | ReactElement
  | string
  | number
  | ReactFragment
  | ReactPortal
  | boolean
  | null
  | undefined;

JSX.Element

declare global {
  namespace JSX {
    interface Element extends React.ReactElement<any, any> { }
  }
}
drizzle96 commented 2 months ago

React.ReactElement, JSX.Element, React.ReactNode는 모두 리액트 요소를 나타내는 타입입니다. 다만 각 타입은 아래처럼 약간의 차이를 보입니다.

  1. React.ReactElement

    • 함수 컴포넌트의 반환 타입으로 셋 중 기본 베이스입니다.
    • 제네릭으로 props, type 타입을 받습니다.
  2. JSX.Element

    • React.ReactElement의 자식 타입으로 props,type 타입에 any가 들어갑니다.
    • 글로벌 네임스페이스에 정의되어 있어 외부 라이브러리에서 컴포넌트 타입을 재정의 할 수 있는 유연성을 제공합니다.
  3. React.ReactNode

    • React.ReactElement 외에도 boolean, string, number 등의 여러 타입을 유니온으로 포함하는 넓은 타입입니다.

이런 차이로 인해 각 타입이 주로 활용되는 상황이 다음과 같습니다.

  1. React.ReactNode

    • React.ReactNode는 리액트의 render 함수가 반환할 수 있는 모든 형태를 담고 있습니다.
    • 컴포지션 모델에서 children에는 여러 타입이 들어올 수 있습니다. 이렇게 넓은 형태를 주고 싶을 때 React.ReactNode를 사용할 수 있습니다.
  2. JSX.Element

    • JSX.Element는 props와 type 필드가 any 타입인 ReactElement 타입과 같습니다.
    • 따라서 props를 모르는 컴포넌트를 render props 패턴에 사용할 때 활용할 수 있습니다. (사실 이런 경우에도 제네릭을 주지 않은 React.ReactElement를 사용하면 되긴 합니다)
    • 외부 라이브러리가 자체 컴포넌트 시스템을 갖고 있거나 리액트와 다른 방식으로 JSX를 사용하는 경우에 사용할 수 있습니다.
  3. React.ReactElement

    • props, type을 더 좁게 추론하고 싶을 때 React.ReactElement의 제네릭으로 지정하는 식으로 활용할 수 있습니다.
    • props의 형태를 구체화했기 때문에 props에 접근할 때 목록이 추론되어 표시됩니다.
hyeyoonS commented 2 months ago

우선 JSX에 대해서 살펴볼 필요가 있습니다.

⇒ 즉, JSX는 리액트 엘리먼트를 생성하기 위한 문법이며 트랜스파일러는 JSX 문법을 createElement 메서드 호출문으로 변환하여 리액트 엘리먼트를 생성합니다.

⇒ JSX 코드를 작성하면 해당 코드는 JSX.Element로 변환되고, 이는 React 애플리케이션에서 React.ReactElement로 변환됩니다. React.ReactNode은 React 애플리케이션에서 렌더링 가능한 모든 요소를 포함하는 범용적인 개념이며, JSX.Element는 TypeScript에서 JSX 요소의 타입을 정의하기 위해 사용됩니다.


322454210-1feffd90-4f3e-427b-a1ee-883918d7d08b

React.ReactElement

interface IconProps {
  size: number;
}

interface Props {
  // Item 컴포넌트의 props를 정의.
  // ReactElement의 props 타입으로 IconProps 타입 지정
  icon: React.ReactElement<IconProps>; // icon의 props의 타입은 IconProps
  // icon은 IconProps 타입의 props를 갖는 ReactElement임을 명시.
}

const Item = ({ icon }: Props) => {
  //icon을 props로 받음
  // icon prop으로 받은 컴포넌트의 props에 접근하면, props의 목록이 추론된다
  const iconSize = icon.props.size; // icon의 props에 접근.

  return <li>{icon}</li>;
};

JSX.Element

⇒ 타입스크립트가 JSX를 사용할 때 정의하는 타입입니다.

// JSX.Element 타입을 사용하여 React 컴포넌트에서 자식 컴포넌트를 props로 전달하고,
// 그 자식 컴포넌트의 props에 접근하는 방법

//IconProps을 JSX.Element 타입으로 선언함으로써 해당 prop에는 JSX 문법만 삽입할 수 있다.
interface IconProps {
  icon: JSX.Element;
}

const Item = ({ icon }: IconProps) => {
  // icon을 prop으로 받음.
  // prop으로 받은 컴포넌트의 Iconprops에 접근할 수 있다
  const iconSize = icon.Iconprops.size; // icon의 props에 접근하여 size 속성을 가져옴.
  // icon이 JSX 요소이기 때문에 가능함.

  return <li>{icon}</li>;
};

// icon prop에는 JSX.Element 타입을 가진 요소만 할당할 수 있다
const App = () => {
  return <Item icon={<Icon size={14} />} />; // Icon컴포넌트는 JSX.Element 타입의 요소
};

React.ReactNode

type ReactFragment = {} | Iterable<ReactNode>; // ReactNode의 배열 형태
type ReactNode =
  | ReactChild
  | ReactFragment
  | ReactPortal
  | boolean
  | null
  | undefined;

summerkimm commented 2 months ago

ReactElement

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

ReactNode

type ReactNode =
  | ReactElement
  | string
  | number
  | ReactFragment
  | ReactPortal
  | boolean
  | null
  | undefined;

JSX.Element

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