createPortal

createPortal memungkinkan Anda merender beberapa children ke bagian yang berbeda dari DOM.

<div>
<SomeComponent />
{createPortal(children, domNode)}
</div>

Referensi

createPortal(children, domNode)

Untuk membuat sebuah portal, panggil createPortal, dengan mengoper beberapa JSX, dan DOM node dimana tempat portal tersebut harus dirender:

import { createPortal } from 'react-dom';

// ...

<div>
<p>This child is placed in the parent div.</p>
{createPortal(
<p>This child is placed in the document body.</p>,
document.body
)}
</div>

Lihat lebih banyak contoh di bawah ini.

Sebuah portal hanya mengubah penempatan fisik dari DOM node. Dalam hal lain, JSX yang Anda render ke dalam portal bertindak sebagai child node dari komponen React yang merendernya. Sebagai contoh, children dapat mengakses konteks yang disediakan oleh pohon induk, dan kejadian bubble up dari children ke induk sesuai dengan pohon React.

Parameters

  • children: Apapun yang dapat di-render dengan React, seperti sepotong JSX (misalnya <div /> atau <SomeComponent />), sebuah Fragment (<>...</>), sebuah string atau angka, ataupun sebuah larik.

  • domNode: Beberapa DOM node, seperti yang dikembalikan oleh document.getElementById(). Node tersebut harus sudah ada. Melewatkan DOM node yang berbeda selama pembaruan akan menyebabkan konten portal dibuat ulang.

Pengembalian

createPortal mengembalikan sebuah React node yang dapat disertakan ke dalam JSX atau dikembalikan dari komponen React. Jika React menemukannya dalam keluaran render, React akan menempatkan children yang disediakan di dalam domNode.

Peringatan

  • Kejadian dari portal menyebar sesuai dengan pohon React, bukan pohon DOM. Sebagai contoh, jika Anda mengklik di dalam sebuah portal, dan portal tersebut dibungkus dengan <div onClick>, maka handler onClick akan dijalankan. Jika hal ini menyebabkan masalah, hentikan perambatan kejadian dari dalam portal, atau pindahkan portal itu sendiri ke atas dalam pohon React.

Penggunaan

Merender ke bagian yang berbeda dari DOM

Portal memungkinkan komponen Anda merender beberapa komponen children ke tempat yang berbeda dalam DOM. Hal ini memungkinkan bagian dari komponen Anda “keluar” dari wadah apa pun yang ada. Sebagai contoh, sebuah komponen dapat menampilkan dialog modal atau tooltip yang muncul di atas dan di luar halaman lainnya.

Untuk membuat portal, render hasil dari createPortal dengan beberapa JSX dan DOM node ke tempat yang seharusnya:

import { createPortal } from 'react-dom';

function MyComponent() {
return (
<div style={{ border: '2px solid black' }}>
<p>This child is placed in the parent div.</p>
{createPortal(
<p>This child is placed in the document body.</p>,
document.body
)}
</div>
);
}

React akan meletakkan DOM node untuk JSX yang Anda berikan di dalam DOM node yang Anda sediakan.

Tanpa portal, elemen <p> yang kedua akan ditempatkan di dalam <div> induk, tetapi portal memindahkannya ke dalam document.body:

import { createPortal } from 'react-dom';

export default function MyComponent() {
  return (
    <div style={{ border: '2px solid black' }}>
      <p>This child is placed in the parent div.</p>
      {createPortal(
        <p>This child is placed in the document body.</p>,
        document.body
      )}
    </div>
  );
}

Perhatikan bagaimana paragraf kedua secara visual muncul di luar induk <div> dengan batas. Jika Anda memeriksa struktur DOM dengan alat bantu pengembang, Anda akan melihat bahwa elemen <p> kedua ditempatkan langsung ke dalam <body>:

<body>
<div id="root">
...
<div style="border: 2px solid black">
<p>This child is placed inside the parent div.</p>
</div>
...
</div>
<p>This child is placed in the document body.</p>
</body>

Portal hanya mengubah penempatan fisik dari DOM node. Dalam hal lain, JSX yang Anda render ke dalam portal bertindak sebagai child node dari komponen React yang merendernya. Sebagai contoh, children dapat mengakses konteks yang disediakan oleh pohon induk, dan kejadian bubble up dari children ke induk sesuai dengan pohon React.


Merender dialog modal dengan portal

Anda dapat menggunakan portal untuk membuat dialog modal yang mengapung di atas sisa halaman, bahkan jika komponen yang memanggil dialog berada di dalam wadah dengan overflow: hidden atau style lain yang bercampur dialog.

Pada contoh ini, dua kontainer memiliki style yang bercampur dialog modal, tetapi yang dirender ke dalam portal tidak terpengaruh karena, di dalam DOM, modal tidak terkandung di dalam elemen JSX induk.

import NoPortalExample from './NoPortalExample';
import PortalExample from './PortalExample';

export default function App() {
  return (
    <>
      <div className="clipping-container">
        <NoPortalExample  />
      </div>
      <div className="clipping-container">
        <PortalExample />
      </div>
    </>
  );
}

Pitfall

Penting untuk memastikan bahwa aplikasi Anda dapat diakses saat menggunakan portal. Misalnya, Anda mungkin perlu mengatur fokus keyboard agar pengguna dapat memindahkan fokus ke dalam dan ke luar portal secara alami.

Ikuti Praktik Penulisan Modal WAI-ARIA saat membuat modal. Jika Anda menggunakan paket komunitas, pastikan paket tersebut dapat diakses dan mengikuti panduan ini.


Merender komponen React ke dalam non-React server markup

Portal dapat berguna jika akar React Anda hanya merupakan bagian dari halaman statis atau halaman yang di-render oleh server yang tidak dibangun dengan React. Sebagai contoh, jika halaman Anda dibangun dengan kerangka kerja server seperti Rails, Anda dapat membuat area interaktivitas di dalam area statis seperti sidebar. Dibandingkan dengan memiliki beberapa akar React yang terpisah, portal memungkinkan Anda memperlakukan aplikasi sebagai satu pohon React dengan state yang sama meskipun bagian-bagiannya di-render ke bagian yang berbeda di dalam DOM.

import { createPortal } from 'react-dom';

const sidebarContentEl = document.getElementById('sidebar-content');

export default function App() {
  return (
    <>
      <MainContent />
      {createPortal(
        <SidebarContent />,
        sidebarContentEl
      )}
    </>
  );
}

function MainContent() {
  return <p>This part is rendered by React</p>;
}

function SidebarContent() {
  return <p>This part is also rendered by React!</p>;
}


Merender komponen React ke dalam non-React DOM node

Anda juga dapat menggunakan portal untuk mengelola konten DOM node yang dikelola di luar React. Sebagai contoh, misalkan Anda mengintegrasikan dengan widget peta non-React dan Anda ingin me-render konten React di dalam popup. Untuk melakukan ini, deklarasikan variabel popupContainer state untuk menyimpan DOM node yang akan Anda render:

const [popupContainer, setPopupContainer] = useState(null);

Saat Anda membuat widget pihak ketiga, simpan DOM node yang dikembalikan oleh widget agar Anda dapat merendernya:

useEffect(() => {
if (mapRef.current === null) {
const map = createMapWidget(containerRef.current);
mapRef.current = map;
const popupDiv = addPopupToMapWidget(map);
setPopupContainer(popupDiv);
}
}, []);

Hal ini memungkinkan Anda menggunakan createPortal untuk merender konten React ke dalam popupContainer setelah konten tersebut tersedia:

return (
<div style={{ width: 250, height: 250 }} ref={containerRef}>
{popupContainer !== null && createPortal(
<p>Hello from React!</p>,
popupContainer
)}
</div>
);

Berikut ini contoh lengkap yang bisa Anda mainkan:

import { useRef, useEffect, useState } from 'react';
import { createPortal } from 'react-dom';
import { createMapWidget, addPopupToMapWidget } from './map-widget.js';

export default function Map() {
  const containerRef = useRef(null);
  const mapRef = useRef(null);
  const [popupContainer, setPopupContainer] = useState(null);

  useEffect(() => {
    if (mapRef.current === null) {
      const map = createMapWidget(containerRef.current);
      mapRef.current = map;
      const popupDiv = addPopupToMapWidget(map);
      setPopupContainer(popupDiv);
    }
  }, []);

  return (
    <div style={{ width: 250, height: 250 }} ref={containerRef}>
      {popupContainer !== null && createPortal(
        <p>Hello from React!</p>,
        popupContainer
      )}
    </div>
  );
}