TIL 118. Next.js에서 createPortal

isk·2023년 9월 12일
0

TIL

목록 보기
119/122
post-custom-banner

react와 next에서의 createPortal 사용법이 조금 다르다.


_document.tsx 파일에 아래처럼 포탈 div를 만들어준다.

// _document.tsx

import {Head, Html, Main, NextScript} from "next/document";

export default function Document() {
  return (
    <Html lang="en">
      <Head></Head>
      <title>3BUILD</title>
      <body style={{padding: "0px 20px"}}>
        <Main />
        <div id="modal"></div>
        <div id="toast"></div>
        <NextScript />
      </body>
    </Html>
  );
}

포탈을 구현할 곳(<div id="toast"></div>)에 아래 코드로 포탈을 열어준다.

// react에서의 ToastPortal.tsx

import {useEffect, useState} from "react";
import ReactDom from "react-dom";

function ToastPortal({children}: any) {
  const el = document.getElementById("toast") as HTMLDivElement;
  return ReactDom.createPortal(<div>{children}</div>, el);
}

export default ToastPortal;

근데 위 코드를 Next.js에서 사용하면
document is not defined
Hydration failed because the initial UI does not match what was rendered on the server

이슈가 표시될 것이다.

첫번째 이슈는 Next는 별도의 설정을 하지 않는다면 SSR / CSR을 동시에 진행하기 때문에 SSR 과정에서 document를 탐색할 수 없기 때문에 생기고,
두번째 이슈는 SSR에서 생성한 정적 페이지와 CSR에서 초기에 읽어낸 웹페이지의 코드가 서로 상이하여 생기는 문제다.

그래서 아래처럼 작성한다.

// next에서의 ToastPortal.tsx

import {useEffect, useState} from "react";
import ReactDom from "react-dom";

function ToastPortal({children}: any) {
  const [isCSR, setIsCSR] = useState<boolean>(false); // 두번째 이슈해결

  useEffect(() => { // 두번째 이슈해결
    setIsCSR(true);
  }, []);

  if (typeof window === "undefined") return <></>; // 첫번째 이슈해결
  if (!isCSR) return <></>; // 두번째 이슈해결

  const el = document.getElementById("toast") as HTMLDivElement;

  return ReactDom.createPortal(<div>{children}</div>, el);
}

export default ToastPortal;

그 이후에는 리엑트와 마찬가지로 구현하면 된다.

post-custom-banner

0개의 댓글