Portals

e-pong:)·2022년 11월 16일
0

1. Portal란?

1-1) Portal 정의

Portal은 부모 컴포넌트의 DOM 계층 구조 바깥에 있는 DOM 노드로 자식을 렌더링 하는 최고의 방법을 제공합니다.

ReactDOM.createPortal(child, container)

첫 번째 인자(child)는 엘리먼트, 문자열, 혹은 fragment와 같은 어떤 종류이든 렌더링할 수 있는 React 자식입니다. 두 번째 인자(container)는 DOM 엘리먼트입니다.

1-2) 사용하는 이유

일반적으로 React는 부모 컴포넌트가 렌더링되면 자식 컴포넌트가 렌더링되는 tree 구조를 가지고 있다.
하지만 때때로 이러한 tree 구조는 불편함을 가져다 주는데, 이럴 때 부모-자식 관계는 유지하지만 독립적인 위치에서 렌더링을 하고 싶은 경우가 생긴다.

대표적으로 modal은 부모 컴포넌트의 스타일링 속성에 제약을 받아 z-index 등으로 번거로운 후처리를 해줘야 한다.
이러한 상황에서 portal을 통해 독립적인 구조와 부모-자식 관계를 동시에 유지할 수 있다면, z-index 등 부모 컴포넌트의 제약에서 벗어날 수 있다.

2. Portal을 이용한 Modal 만들기

/public/index.html

모달이 생성 될 루트 지정

  <body>
    <div id="overlays"></div>
    <div id="root"></div>
  </body>

/src/components/UI/Modal.js

모달 컴포넌트 작성

import { Fragment } from 'react';
import ReactDOM from 'react-dom';

import classes from './Modal.module.css';

const Backdrop = (props) => {
  return <div className={classes.backdrop} onClick={props.onClose}/>;
};

const ModalOverlay = (props) => {
  return (
    <div className={classes.modal}>
      <div className={classes.content}>{props.children}</div>
    </div>
  );
};

const portalElement = document.getElementById('overlays');

const Modal = (props) => {
  return (
    <Fragment>
      {ReactDOM.createPortal(<Backdrop onClose={props.onClose} />, portalElement)}
      {ReactDOM.createPortal(
        <ModalOverlay>{props.children}</ModalOverlay>,
        portalElement
      )}
    </Fragment>
  );
};

export default Modal;

/src/components/UI/Modal.moudle.css

모달 css 작성

.backdrop {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100vh;
    z-index: 20;
    background-color: rgba(0, 0, 0, 0.75);
  }
  
  .modal {
    position: fixed;
    top: 20vh;
    left: 5%;
    width: 90%;
    background-color: white;
    padding: 1rem;
    border-radius: 14px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25);
    z-index: 30;
    animation: slide-down 300ms ease-out forwards;
  }
  
  @media (min-width: 768px) {
    .modal {
      width: 40rem;
      left: calc(50% - 20rem);
    }
  }
  
  @keyframes slide-down {
    from {
      opacity: 0;
      transform: translateY(-3rem);
    }
    to {
      opacity: 1;
      transform: translateY(0);
    }
  }
profile
말에 힘이 있는 사람이 되기 위해 하루하루, 성장합니다.

0개의 댓글