Portal

Portal

부모 요소 트리 밖에 컴포넌트를 렌더링하는 컴포넌트예요. Portal은 ReactDOM.createPortal API의 래퍼 컴포넌트예요.

출처: 문서

본문

사용법 (Usage)

Portal은 ReactDOM.createPortal API의 래퍼 컴포넌트예요. 어떤 컴포넌트나 요소도 document.body 끝이나 지정한 요소에 렌더링해요. Modal과 Drawer 컴포넌트는 기본적으로 Portal로 감싸져 있어요.

Portal을 사용하면 컴포넌트나 요소를 다른 위치(기본은 document.body 끝)에 렌더링해요. 부모 스타일이 자식에 간섭하는 것을 막고 싶을 때 유용해요. 보통 이런 스타일은 position과 z-index 속성과 관련이 있고, 포털은 모달처럼 고정 위치 컴포넌트에 사용돼요.

import { useState } from 'react';
import { Portal } from '@mantine/core';

function Demo() {
  const [opened, setOpened] = useState(false);

  return (
    <>
      <Portal>
        <div>
          {opened && (
            <div>Your modal content</div>
          )}
        </div>
      </Portal>

      <button onClick={() => setOpened(true)} type="button">
        Open modal
      </button>
    </>
  );
}

위 예시에서 div 요소는 부모 main 밖(닫는 body 태그 앞)에 렌더링되지만 여전히 opened와 onClose prop을 받아요. 이 요소는 부모의 z-index에 영향을 받지 않아요.

타겟 노드 재사용 (Reuse target node)

기본적으로 Portal은 모든 인스턴스에 대해 동일한 타겟 노드를 재사용해요 (reuseTargetNode={true}). 인스턴스마다 새 타겟 노드를 만들려면 reuseTargetNode={false}를 설정해요. 아래 예시에서 세 문단은 모두 별도의 타겟 노드에 렌더링돼요.

import { Portal } from '@mantine/core';

function Demo() {
  return (
    <>
      <Portal reuseTargetNode={false}>First</Portal>
      <Portal reuseTargetNode={false}>Second</Portal>
      <Portal reuseTargetNode={false}>Third</Portal>
    </>
  );
}

타겟 DOM 노드 지정 (Specify target DOM node)

target prop으로 포털이 렌더링될 DOM 노드를 지정할 수 있어요.

import { Portal } from '@mantine/core';

const container = document.createElement('div');
document.body.appendChild(container);

function Demo() {
  return <Portal target={container}>My portal</Portal>;
}

또는 셀렉터를 지정해 기존 요소에 포털을 렌더링할 수도 있어요.

import { Portal } from '@mantine/core';

function Demo() {
  return <Portal target="#my-container">My portal</Portal>;
}

타겟 요소를 지정하지 않으면 각 Portal 컴포넌트마다 새 요소가 생성되어 document.body에 추가돼요.

서버 사이드 렌더링 (Server side rendering)

createPortal은 서버 사이드 렌더링 중에는 지원되지 않아요. Portal 내부의 모든 컴포넌트는 애플리케이션이 DOM에 마운트된 후에만 렌더링돼요.

OptionalPortal 컴포넌트

OptionalPortal 컴포넌트를 사용하면 children을 Portal에 렌더링할지 여부를 설정할 수 있어요. Portal 컴포넌트와 동일한 prop을 받아요.

import { OptionalPortal } from '@mantine/core';

function Demo() {
  return (
    <>
      <OptionalPortal withinPortal>This text is rendered in Portal</OptionalPortal>
      <OptionalPortal withinPortal={false}>This text is rendered as regular child</OptionalPortal>
    </>
  );
}

더 알아보기 (Learn more)