portal 활용한 모달 개발 (feat nextjs)

jinabbb·2023년 2월 27일
0
post-thumbnail

프로젝트 진행중 모달을 사용하는게 많아서 개발 겸 기록

Portal?

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

보통 모달을 호출하는 컴포넌트안에서 모달을 작성하는데 이때 자식 엘리먼트로 렌더링 하지 않고 외부에서 렌더링 시키려고 사용한다. 보통 모달은 부모 엘리먼트랑은 독립적이니까 자식 엘리먼트로 렌더링 되면 시맨틱하지 않다

Portal 사용법

ReactDOM.createPortal(child, container)

간단하다 렌더링할 child엘리먼트를 container(외부 엘리먼트)에 부착하게 된다.
다만 nextjs는 기본적으로 ssr을 지원하기 때문에 container가 아직 렌더링 되지 않았음을 주의해야한다.

nextjs에서는 _app 이후에 실행되는 _document.tsx에 포탈로 사용할 엘리먼트를 작성해야한다.

//_document.tsx
import { Html, Head, Main, NextScript } from 'next/document';

export default function Document() {
  return (
    <Html lang="en">
      <Head />
      <body>
        <Main />
        <div id="modal-root" />
        <NextScript />
      </body>
    </Html>
  );
}

#modal-root를 container로 사용할 예정

모달구현

//Modal.tsx
import * as React from 'react';
import { createPortal } from 'react-dom';

import ModalOverlay from './ModalOverlay';
import ModalWrapper from './ModalWrapper';

export interface IModalProps {
  children: React.ReactNode;
  hasOverlay: boolean;
}

export default function Modal({ children, hasOverlay }: IModalProps) {
  const ref = React.useRef<Element | null>(null);
  const [mounted, setMounted] = React.useState(false);

  React.useEffect(() => {
    ref.current = document.querySelector<HTMLElement>('#modal-root');
    setMounted(true);
  }, []);
  return mounted && ref.current
    ? createPortal(
        <>
          <ModalOverlay visible={hasOverlay} />
          <ModalWrapper>{children}</ModalWrapper>
        </>,
        ref.current
      )
    : null;
}

useEffect를 활용해서 엘리먼트가 부착된 이후에 modal-root에 모달을 달아준다.

profile
개발

0개의 댓글