react.js

[react 리액트] Portal 을 사용하여 모달창 만들기

코복이 2023. 6. 28. 19:09
728x90

 

1. Portal 은 부모 컴포넌트 DOM 계층 구조 밖으로 자식을 랜더링한다

모달의 위치는 어떤 것에도 간섭받지 않는 root 밖으로 와야 한다.
id 값으로 포탈 위치를 정할 수 있는데, 위치는 root에 간섭받지 않도록 root와 형제자리로 한다.

 

index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="theme-color" content="#000000" />
    <meta
      name="description"
      content="Web site created using create-react-app"
    />
    <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
    <title>React App</title>
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>

    <!-- 모달이 올 위치는 root와 형제자리 -->
    <div id="overlays"></div>
    <div id="root"></div>
  </body>
</html>

 

 

 

 

2. 모달 컴포넌트에서 ReactDOM을 호출하고 CratePortal 메소드를 실행한다.

Modal.js

(모달 컴포넌트 = 현재 코드에선 모달 '프레임'으로 이제 이걸로 원하는 컴포넌트를 감싸면 됨)

import { Fragment } from "react";
import ReactDOM from "react-dom";
import styles from "./Modal.module.css";


/* Backdrop, ModalOverlay는 모달 디자인 컴포넌트 */
// 뒷배경
const Backdrop = () => {
  return <div className={styles.Backdrop}></div>;
};

// 모달창 ({props.children}으로 감쌀 내용을 가져와야 함)
const ModalOverlay = (props) => {
  return (
    <div className={styles.modal}>
      <div className={styles.content}>{props.children}</div>
    </div>
  );
};

/* index.html에서 모달을 만들기 위해 추가한 overlay에 접근 */
const portalElement = document.getElementById("overlays");

/* ReactDOM.createPortal() 메소드는 두개 인자를 받음 (포탈 위치로 보낼 것, 포탈 위치) */
// Fragment는 <></> 빈배열과 동일
export default function Modal(props) {
  return (
    <Fragment>
      {ReactDOM.createPortal(<Backdrop />, portalElement)}
      {ReactDOM.createPortal(
        <ModalOverlay>{props.children}</ModalOverlay>,
        portalElement
      )}
    </Fragment>
  );
}

 

 

 

3. boolean 타입의 useState로 스위치 껐다켰다 구현해주면 끝.

import React, { Fragment, useState } from "react";
import Header from "./Components/Layout/Header";
import Meals from "./Components/Meals/Meals";
import Cart from "./Components/Cart/Cart";

function App() {
  /* setter 함수에 true가 들어가면 Modal 켜지고 false가 들어오면 꺼짐 */
  const [cardIsShown, setCardIsShown] = useState(false)
  const handleShowCard = () => {
    setCardIsShown(true)
  }
  const handleHideCard = () => {
    setCardIsShown(false)
  }

  return (
    <Fragment>
      {cardIsShown && <Cart handleHideCard={handleHideCard}/>}
      <Header handleShowCard={handleShowCard} />
      <main>
        <Meals />
      </main>
    </Fragment>
  );
}

export default App;

 

 

 

4. 완성

모달이 root 내부가 아닌 바깥으로 온 것을 볼 수 있다.

 

728x90