Next.js에서 React portal 사용

Hwalyo·2022년 8월 25일
2
post-custom-banner

Next.js에서 ReactPortal을 사용하기 위해서는 _app 이후에 동작하는 _document을 작성해야 합니다.
_document는 공통적으로 활용할 <head> (Ex. 메타 태그)나 <body> 태그 안에 들어갈 내용들을 커스텀할때 활용합니다.

1. 최상위폴더/_document.tsx

import Document, { Html, Head, Main, NextScript } from "next/document";
class MyDocument extends Document {
  render() {
    return (
      <Html>
        <Head />
        <body>
          <Main />
      	  //document에 _modal이란 div가 생성된다.
          <div id="_modal"></div>
          <NextScript />
        </body>
      </Html>
    );
  }
}

export default MyDocument;

2. Modal Component portal 사용

import { Fragment, PropsWithChildren } from "react";
import ReactDOM  from "react-dom"; 
import styles from './Modal.module.css'
interface ModalProps{
    onClose : () => void
}
const BackDrop = (props : ModalProps) =>{
    return(
        <div className={styles.backdrop} onClick={props.onClose}></div>
    )
}

const ModalOverlay = ({children}: PropsWithChildren) =>{
    return (
        <div className={styles.modal}>
            <div className={styles.content}>
            {children}
            </div>
        </div>
    )
}
const Modal = ( props : PropsWithChildren<ModalProps>) =>{
    const selectedElement = document.getElementById('_modal');
    if(selectedElement === null){
      	//null에 대한 에러 처리를 할 수 있다.
        return <div></div>
    }
    return <Fragment>
        {ReactDOM.createPortal(<BackDrop onClose={props.onClose}/>, selectedElement)}
        {ReactDOM.createPortal(<ModalOverlay>{props.children}</ModalOverlay>, selectedElement)}
    </Fragment>
}

export default Modal;

3. 모달 내부 구현

import Link from "next/link";
import Modal from "../UI/Modal";

interface MenuItems {
  url: string;
  menu: string;
}
const menus: MenuItems[] = [
  { url: "/", menu: "Home" },
  { url: "/skills", menu: "Skills" },
  { url: "/projects", menu: "Project" },
];
interface Iprops{
    onClose: () => void
}
const Menu = (props :Iprops) => {
  return (
    <Modal onClose={props.onClose}>
      <ul onClick={props.onClose}>
        {menus.map((menuObj) => (
          <li key={menuObj.menu}>
            <Link href={menuObj.url}>{menuObj.menu}</Link>
          </li>
        ))}
      </ul>
    </Modal>
  );
};

export default Menu;

4. 구현된 Modal을 렌더링하는 컴포넌트

const [menuSelected, setMenuSelected] = useState(false);
  const menuHandler = () =>{
    setMenuSelected(true);
  }
  const closeMenuHanlder = () =>{
    setMenuSelected(false);
  }
  return ( 
    <div>
    { menuSelected && <Menu onClose={closeMenuHanlder}/>}
      <div className={styles.menuIcon}>
        <Image onClick={menuHandler} width={"35px"} height={"35px"} src={menuIcon} alt={"메뉴 네비게이션입니다."}/>
      </div>
    </div>
  )
profile
꾸준하게글을작성해보자
post-custom-banner

0개의 댓글