ReactNextCentral

포털

Published on
리액트의 `createPortal`을 사용하여 DOM의 다른 부분에 모달과 같은 UI 요소를 렌더링하는 방법과 관련 예시를 설명합니다.
Table of Contents

ReactDOM.createPortal 사용하기:

const modalRoot = document.getElementById("modal-root") as HTMLElement;
// html 파일에 'modal-root' id를 가진 div가 있다고 가정합니다;

export class Modal extends React.Component<{ children?: React.ReactNode }> {
  el: HTMLElement = document.createElement("div");

  componentDidMount() {
    modalRoot.appendChild(this.el);
  }

  componentWillUnmount() {
    modalRoot.removeChild(this.el);
  }

  render() {
    return ReactDOM.createPortal(this.props.children, this.el);
  }
}

타입스크립트 플레이그라운드에서 보기

훅을 사용하는 방법

위와 동일하지만 훅을 사용하여 구현한 예시입니다.

import { useEffect, useRef, ReactNode } from "react";
import { createPortal } from "react-dom";

const modalRoot = document.querySelector("#modal-root") as HTMLElement;

type ModalProps = {
  children: ReactNode;
};

function Modal({ children }: ModalProps) {
  // ref를 사용해 div 요소를 한 번만 생성합니다
  const elRef = useRef<HTMLDivElement | null>(null);
  if (!elRef.current) elRef.current = document.createElement("div");

  useEffect(() => {
    const el = elRef.current!; // 절대 null이 아니기 때문에 non-null 단언 사용
    modalRoot.appendChild(el);
    return () => {
      modalRoot.removeChild(el);
    };
  }, []);

  return createPortal(children, elRef.current);
}

타입스크립트 플레이그라운드에서 보기

모달 컴포넌트 사용 예시:

import { useState } from "react";

function App() {
  const [showModal, setShowModal] = useState(false);

  return (
    <div>
      // 정적 html 파일에 이 부분을 넣을 수도 있습니다
      <div id="modal-root"></div>
      {showModal && (
        <Modal>
          <div
            style={{
              display: "grid",
              placeItems: "center",
              height: "100vh",
              width: "100vh",
              background: "rgba(0,0,0,0.1)",
              zIndex: 99,
            }}
          >
            모달입니다!{" "}
            <button
              style={{ background: "papyawhip" }}
              onClick={() => setShowModal(false)}
            >
              닫기
            </button>
          </div>
        </Modal>
      )}
      <button onClick={() => setShowModal(true)}>모달 보기</button>
      // 앱의 나머지 부분
    </div>
  );
}
예시의 배경

이 예시는 React 문서의 포털을 통한 이벤트 버블링 예시를 바탕으로 합니다.