TIL_67_Portal

JIEUN·2021년 4월 16일
0

Learn React Portal In 12 Minutes By Building A Modal

위 영상을 기반으로 작성하였음.

기업협업 4일차. 함께 프로젝트를 진행할 개발자 분께서 모달창 관련하여 왜 포탈을 사용하게 됐는지를 이해하는 것이 중요하다고 말씀해주셨다. 준우님의 공유 덕분에 위 영상을 보게 되었다.

보자마자 눈에 띄는 문제점을 찾을 수 있을 것이다.
모달창이 떴을 때 Other Content가 가려지지 않는 것을 확인할 수 있다. 코드를 한번 봐보자.

// App.js
import React, { useState } from "react";
import Modal from "./Modal";

const BUTTON_WRAPPER_STYLES = {
  position: "relative",
  zIndex: 1,
};

const OTHER_CONTENT_STYLES = {
  position: "relative",
  zIndex: 2,
  backgroundColor: "red",
  padding: "10px",
};

export default function App() {
  const [isOpen, setIsOpen] = useState(false);
  return (
    <>
      <div style={BUTTON_WRAPPER_STYLES}>
        <button onClick={() => setIsOpen(true)}>Open Modal</button>

        <Modal open={isOpen} onClose={() => setIsOpen(false)}>
          Fancy Modal
        </Modal>
      </div>

      <div style={OTHER_CONTENT_STYLES}>Other Content</div>
    </>
  );
}
// Modal.js
import React from "react";

const MODAL_STYLES = {
  position: "fixed",
  top: "50%",
  left: "50%",
  transform: "translate(-50%, -50%)",
  backgroundColor: "#FFF",
  padding: "50px",
  zIndex: 1000,
};

const OVERLAY_STYLES = {
  position: "fixed",
  top: 0,
  left: 0,
  right: 0,
  bottom: 0,
  backgroundColor: "rgba(0, 0, 0, .7)",
  zIndex: 1000,
};

export default function Modal({ open, children, onClose }) {
  if (!open) return null;

  return (
    <>
      <div style={OVERLAY_STYLES} />
      <div style={MODAL_STYLES}>
        <button onClick={onClose}>Close Modal</button>
        {children}
      </div>
    </>
  );
}

위의 코드를 보면, App.js 파일에서 OTHER_CONTENT_STYLES 에 zIndex에 2를 준 것을 확인할 수 있다.
그리고 아래 Modal.js 파일에서 MODAL_STYLES 에 zIndex에 1000을 준 것을 확인할 수 있다. 모달의 zIndex가 훨씬 높은데도 왜 가려지지 않는 걸까?
그 이유는 Modal이 App.js 파일에 상속되어있기 때문이다.
Modal은 App.js 코드를 보면 알 수 있듯이 div로 감싸져있다. Modal에게 아무리 높은 zIndex를 준다하여도 하위 컴퍼넌트가 상위 컴퍼넌트를 덮을 순 없는 것이다. Other Content 보다 zIndex가 낮은 BUTTON_WRAPPER_STYLES는 OTHER_CONTENT_STYLES에 의해 가려진 것일 뿐이다.

포탈을 통해 이 문제를 해결해보자.

// index.html
<body>
    <div id="root"></div>
    <div id="portal"></div>
  </body>
</html>

index.html 의 root 아래에 "portal" 이란 id를 가진 div를 생성해준다.

// Modal.js
import React from "react";
import ReactDom from "react-dom";
//  ReactDom을 import 를 해주고

const MODAL_STYLES = {
  position: "fixed",
  top: "50%",
  left: "50%",
  transform: "translate(-50%, -50%)",
  backgroundColor: "#FFF",
  padding: "50px",
  zIndex: 1000,
};

const OVERLAY_STYLES = {
  position: "fixed",
  top: 0,
  left: 0,
  right: 0,
  bottom: 0,
  backgroundColor: "rgba(0, 0, 0, .7)",
  zIndex: 1000,
};

export default function Modal({ open, children, onClose }) {
  if (!open) return null;

  return ReactDom.createPortal(
    //  리턴 옆에 ReactDom.createPortal 를 선언.
    <>
      <div style={OVERLAY_STYLES} />
      <div style={MODAL_STYLES}>
        <button onClick={onClose}>Close Modal</button>
        {children}
      </div>
    </>,
    document.getElementById("portal")
  //  document.getElementById("portal") 선언.
  );
}


오 해결!

리액트 - Portals 를 통한 부모 컴포넌트의 외부 DOM 에 컴포넌트 렌더링하기

0개의 댓글