OLD/React

[React] React 여러 기능들 (Fragment, Portal)

joseph0926 2023. 2. 25. 15:26

이 글에서는 중요하지만, 그냥 지나치기 쉬운 기능들을 정리해보려한다.

 

이 글에서 알아볼 것

  • Fragment
  • Portal

 

1. Fragment

앞서 우리는 리액트의 기본개념을 알아보면서 JSX 코드에 대해서 살펴보았다.

JSX 코드는 자바스크립트 코드와 HTML 코드를 동시에 활용할 수 있다는 점에서 굉장히 자유로운 개념이라  느꼈었다.

하지만, 이런 JSX 코드에도 제한사항이 존재한다.

 

  • JSX 코드 제한사항
    • 루트 수준의 JSX 요소들이 인접해있으면(래핑되어있지않으면) 에러 발생
    • 즉, 루트 수준의 JSX는 반드시 한개여야한다
      • 왜냐하면, 이를 반환하거나, 변수등에 저장하기 위한 제약조건이다.
      • 이렇게 글로만 보면 어렵게 보이거나, 잘 와닫지 않지만 자바스크립트를 생각해보면 쉽게 이해가 간다.
        • 자바스크립에서 return은 오직 하나의 값만 가능했다. (객체, 배열또한 하나의 값이다.)
        • 리액트도 결국 index.js에서 return을 하여 index.html에서 렌더링하는 방식이라는 걸 앞의 글을 통해서 우리는 알고있다.
          • 즉, 리액트도 결국 return은 오직 하나의 값만 가능하다는 뜻이다.
    • 해결방법은 뭘까??
      1. 간단하게 루트 수준의 JSX를 하나로 만들고 래핑하면된다 (div등으로..)
      2. return을 배열로한다…
        • 하지만 이 방법은 잘 사용하지 않는다.
        • 왜냐하면, 바로 앞에 글에서 배열로 작업할 때 key prop을 고려해야한다는 것을 살펴봤듯이..굉장히 귀찮아진다.
    • 그러면, 1번을 이용하면 모든것이 해결된걸까? 아니다, 이는 다른 문제인 div soup라는 문제를 낳는다.
      • 1~2번정도 div로 감싼다고 문제가 발생하지 않는다, 다만 앱이 커져서 div로 10번정도 감싼다고 생각해보자..
      • 여기서 사용된 div중에 의미가 있는 div도 존재하겠지만. 대부분이 래핑을 위한 의미없는 div다
        • 이는 스타일링에 영향을 줄 수 있다
        • 성능 저하의 문제도 발생할 수 있다.
    • 그러면 궁극적인 해결방법은 뭘까?
  • Fragment
    • 앞서 살펴본 제한사항을 궁극적으로 해결하는 방법은 하나다.
      • html 요소를 DOM에 렌더링해주지 않는것으로 감싸주면 된다…근데 그게 뭘까?

1. Wrapper 컴포넌트

const Wrapper = (props) => {
  return props.children;
};

export default Wrapper;
  • Wrapper라는 컴포넌트를 생성하고 오직 props.children만을 반환준다
  • 그리고 이 컴포넌트를 사용하여 div대신 감싸준다

2. Fragment

  • 1번과 똑같은 행동을 하는 컴포넌트다.
    • 차이점은 위에는 우리가 만든것이고 Fragment는 리액트에서 제공해준다는 점이다.

3. <> ~ </>

  • 마찬가지로 위에 기능들과 똑같은 행동을 한다
    • 다만, 이방법은 프로젝트 설정에 따라 지원하지 않을 수도 있다.

 

2. Portals

<Fragment>
      <div className="modal">
        <h2>Dummmy Text</h2>
      </div>
      <form>
        <label></label>
        <input></input>
      </form>
    </Fragment>
  • 위의 코드는 문제가 있을까?
    • 없다…없지만 모달부분이 약간 거슬린다
      • 모달은 페이지 위에 표시되는 오버레이다...즉, 모든 것 위에 존재해야한다.
      • 근데 위의 코드는
        1. 다른 코드와 중첩 및 인접해있다 → 의미적으로 좋은 코드가 아님
        2. 상황에 따라 다르겠지만 해당 코드가 html 요소 매우 안쪽에 위치되어있을 수도 있다. → 구조적으로 좋은 코드가 아님
        3. 등의 이유로 의도와 다른 결과를 발생시킬 수 있다
    • 이때 사용하는 기능이 Portal이다.
  • Portal
    • 위의 문제를 해결해주는 것이 Portal 기능이라 말했다. 어떻게 사용할까?
    • Portal은 두가지가 필요하다
      • 컴포넌트를 이동시킬 장소
      • 컴포넌트에게 그 곳에서 포털을 가져야 한다고 알려한다

컴포넌트를 이동시킬 장소란?

=> public 폴더에 존재하는 index.html에 루트 앞 위치다.

<body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="backdrop-root"></div>
    <div id="modal-root"></div>
    <div id="root"></div>

 

컴포넌트에게 그 곳에서 포털을 가져야 한다고 알려한다

const Backdrop = (props) => {
  return <div className={classes.backdrop} onClick={props.onConfirm}></div>;
};
const Modal = (props) => {
  return (
    <Card className={classes.modal}>
      <header className={classes.header}>
        <h2>{props.title}</h2>
      </header>
      <div className={classes.content}>
        <p>{props.message}</p>
      </div>
      <footer className={classes.actions}>
        <Button onClick={props.onConfirm}>Okay</Button>
      </footer>
    </Card>
  );
};

const ErrorModal = (props) => {
  return (
    <Fragment>
      {ReactDOM.createPortal(<Backdrop onConfirm={props.onConfirm}></Backdrop>, document.getElementById("backdrop-root"))}
      {ReactDOM.createPortal(
        <Modal title={props.title} message={props.message} onConfirm={props.onConfirm}></Modal>,
        document.getElementById("modal-root")
      )}
    </Fragment>
  );
};

 

 

이번 글에서 살펴본 내용들도 중요하지만,

앞에서 살펴본 내용들이나, 이후에 살펴볼 내용들에 비해 간단하고 이해가 쉽게가는 내용들이다.

따라서, 이 내용들은 어떻게 사용하는지 보다는 왜 사용하는지를 알고있는것이 중요할 것 같다.