createPortal
createPortal
permite que você renderize alguns filhos em uma parte diferente do DOM.
<div>
<SomeComponent />
{createPortal(children, domNode, key?)}
</div>
Referência
createPortal(children, domNode, key?)
Para criar um portal, chame createPortal
, passando algum JSX e o nó DOM onde ele deve ser renderizado:
import { createPortal } from 'react-dom';
// ...
<div>
<p>Este filho é colocado na div pai.</p>
{createPortal(
<p>Este filho é colocado no corpo do documento.</p>,
document.body
)}
</div>
Um portal apenas altera a colocação física do nó DOM. De todas as outras maneiras, o JSX que você renderiza em um portal atua como um nó filho do componente React que o renderiza. Por exemplo, o filho pode acessar o contexto fornecido pela árvore pai, e os eventos se propagam de filhos para pais de acordo com a árvore React.
Parâmetros
-
children
: Qualquer coisa que pode ser renderizada com o React, como um pedaço de JSX (por exemplo,<div />
ou<SomeComponent />
), um Fragmento (<>...</>
), uma string ou um número, ou um array desses. -
domNode
: Algum nó DOM, como aqueles retornados pordocument.getElementById()
. O nó deve já existir. Passar um nó DOM diferente durante uma atualização fará com que o conteúdo do portal seja recriado. -
opcional
key
: Uma string ou número único a ser usado como a chave do portal.
Retornos
createPortal
retorna um nó React que pode ser incluído no JSX ou retornado de um componente React. Se o React encontrá-lo na saída do render, colocará os children
fornecidos dentro do domNode
fornecido.
Ressalvas
- Eventos de portais se propagam de acordo com a árvore React e não com a árvore DOM. Por exemplo, se você clicar dentro de um portal, e o portal estiver envolvido em
<div onClick>
, o manipuladoronClick
será disparado. Se isso causar problemas, pare a propagação do evento de dentro do portal, ou mova o portal para cima na árvore React.
Uso
Renderizando em uma parte diferente do DOM
Portais permitem que seus componentes renderizem alguns de seus filhos em um lugar diferente no DOM. Isso permite que uma parte do seu componente “escape” de quaisquer contêineres em que possa estar. Por exemplo, um componente pode exibir um diálogo modal ou um tooltip que aparece acima e fora do resto da página.
Para criar um portal, renderize o resultado de createPortal
com algum JSX e o nó DOM onde ele deve ir:
import { createPortal } from 'react-dom';
function MyComponent() {
return (
<div style={{ border: '2px solid black' }}>
<p>Este filho é colocado na div pai.</p>
{createPortal(
<p>Este filho é colocado no corpo do documento.</p>,
document.body
)}
</div>
);
}
O React colocará os nós DOM do JSX que você passou dentro do nó DOM que você forneceu.
Sem um portal, o segundo <p>
seria colocado dentro da <div>
pai, mas o portal “teletransportou” ele para o document.body
:
import { createPortal } from 'react-dom'; export default function MyComponent() { return ( <div style={{ border: '2px solid black' }}> <p>Este filho é colocado na div pai.</p> {createPortal( <p>Este filho é colocado no corpo do documento.</p>, document.body )} </div> ); }
Note como o segundo parágrafo aparece visualmente fora da <div>
pai com a borda. Se você inspecionar a estrutura do DOM com ferramentas de desenvolvedor, verá que o segundo <p>
foi colocado diretamente no <body>
:
<body>
<div id="root">
...
<div style="border: 2px solid black">
<p>Este filho é colocado dentro da div pai.</p>
</div>
...
</div>
<p>Este filho é colocado no corpo do documento.</p>
</body>
Um portal apenas altera a colocação física do nó DOM. De todas as outras maneiras, o JSX que você renderiza em um portal atua como um nó filho do componente React que o renderiza. Por exemplo, o filho pode acessar o contexto fornecido pela árvore pai, e os eventos ainda se propagam de filhos para pais de acordo com a árvore React.
Renderizando um diálogo modal com um portal
Você pode usar um portal para criar um diálogo modal que flutua acima do resto da página, mesmo que o componente que invoca o diálogo esteja dentro de um contêiner com overflow: hidden
ou outros estilos que interferem com o diálogo.
Neste exemplo, os dois contêineres têm estilos que interrompem o diálogo modal, mas o que é renderizado em um portal não é afetado porque, no DOM, o modal não está contido dentro dos elementos JSX pai.
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> </> ); }
Renderizando componentes React em marcação de servidor não-React
Os portais podem ser úteis se sua raiz React for apenas parte de uma página estática ou renderizada no servidor que não é construída com o React. Por exemplo, se sua página for construída com um framework de servidor como Rails, você pode criar áreas de interatividade dentro de áreas estáticas, como barras laterais. Comparado a ter várias raízes React separadas, os portais permitem que você trate o aplicativo como uma única árvore React com estado compartilhado, mesmo que suas partes sejam renderizadas em diferentes partes do 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>Esta parte é renderizada pelo React</p>; } function SidebarContent() { return <p>Esta parte também é renderizada pelo React!</p>; }
Renderizando componentes React em nós DOM não-React
Você também pode usar um portal para gerenciar o conteúdo de um nó DOM que é gerenciado fora do React. Por exemplo, suponha que você esteja integrando com um widget de mapa não-React e deseja renderizar conteúdo React dentro de um popup. Para fazer isso, declare uma variável de estado popupContainer
para armazenar o nó DOM no qual você vai renderizar:
const [popupContainer, setPopupContainer] = useState(null);
Quando você criar o widget de terceiros, armazene o nó DOM retornado pelo widget para que você possa renderizar nele:
useEffect(() => {
if (mapRef.current === null) {
const map = createMapWidget(containerRef.current);
mapRef.current = map;
const popupDiv = addPopupToMapWidget(map);
setPopupContainer(popupDiv);
}
}, []);
Isso permite que você use createPortal
para renderizar conteúdo React dentro de popupContainer
uma vez que ele se torne disponível:
return (
<div style={{ width: 250, height: 250 }} ref={containerRef}>
{popupContainer !== null && createPortal(
<p>Olá do React!</p>,
popupContainer
)}
</div>
);
Aqui está um exemplo completo que você pode experimentar:
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>Olá do React!</p>, popupContainer )} </div> ); }