createPortal

createPortal memungkinkan Anda me-render beberapa anak (children) ke bagian yang berbeda dari DOM.

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

Referensi

createPortal(children, domNode, key?)

Untuk membuat sebuah portal, panggil createPortal, dengan mengoper beberapa JSX, dan simpul DOM dimana tempat portal tersebut harus di-render:

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 contoh selengkapnya di bawah ini.

Sebuah portal hanya mengubah penempatan kerangka dari simpul DOM. Dalam hal lain, JSX yang Anda render ke dalam portal bertindak sebagai simpul anaknya (node child) dari komponen React yang me-render-nya. Sebagai contoh, anaknya (children) dapat mengakses konteks yang disediakan oleh pohon induknya (parent tree), dan kejadian yang bertambah dari anak (children) ke induk (parent) sesuai dengan susunan React.

Parameters

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

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

  • opsional key: Sebuah string atau angka unik yang akan digunakan sebagai kunci portal.

Returns

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

Peringatan

  • Kejadian dari portal menyebar sesuai dengan susunan React, bukan susunan 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 susunan React.

Penggunaan

Me-render ke bagian yang berbeda dari DOM

Portal memungkinkan komponen Anda me-render beberapa komponen anaknya (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 modal dialog atau tooltip yang muncul di atas dan di luar halaman lainnya.

Untuk membuat portal, render hasil dari createPortal dengan beberapa JSX dan simpul DOM 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 simpul DOM untuk JSX yang Anda berikan di dalam simpul DOM yang Anda sediakan.

Tanpa portal, elemen <p> yang kedua akan ditempatkan di dalam induk (parent) <div>, 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 border. Jika Anda memeriksa struktur DOM dengan developer tools, 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 kerangka dari simpul DOM. Dalam hal lain, JSX yang Anda render ke dalam portal bertindak sebagai simpul anaknya (node child) dari komponen React yang me-render-nya. Sebagai contoh, anaknya (children) dapat mengakses konteks yang disediakan oleh pohon induknya (parent tree), dan kejadian yang bertambah dari anak (children) ke induk (parent) sesuai dengan susunan React.


Me-render modal dialog dengan portal

Anda dapat menggunakan portal untuk membuat modal dialog yang mengapung di atas bagian halaman lainnya, 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 modal dialog, tetapi yang di-render ke dalam portal tidak terpengaruh karena, di dalam DOM, modal tidak terkandung di dalam elemen induk (parent) JSX.

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.


Me-render komponen React ke dalam non-React server markup

Portal dapat berguna jika root 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 root React yang terpisah, portal memungkinkan Anda memperlakukan aplikasi sebagai satu susunan 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>;
}


Me-render komponen React ke dalam non-React simpul DOM

Anda juga dapat menggunakan portal untuk mengelola konten simpul DOM 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 state popupContainer untuk menyimpan simpul DOM yang akan Anda render:

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

Saat Anda membuat widget pihak ketiga, simpan simpul DOM yang dikembalikan oleh widget agar Anda dapat me-render-nya:

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 me-render 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>
  );
}