createPortal

Nota bene

Questa pagina è stata tradotta automaticamente e supervisionata da un maintainer. Un’ulteriore revisione da parte della community sarebbe comunque utile. Migliora questa traduzione.

createPortal ti permette di renderizzare alcuni figli in una parte diversa del DOM.

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

Reference

createPortal(children, domNode, key?)

Per creare un portal, chiama createPortal passando del JSX e il nodo DOM in cui deve essere renderizzato:

import { createPortal } from 'react-dom';

// ...

<div>
<p>Questo figlio è posizionato nel div genitore.</p>
{createPortal(
<p>Questo figlio è posizionato nel body del documento.</p>,
document.body
)}
</div>

Vedi altri esempi sotto.

Un portal modifica solo il posizionamento fisico del nodo DOM. Per il resto, il JSX che renderizzi in un portal si comporta come un nodo figlio del componente React che lo renderizza. Ad esempio, il figlio può accedere al context fornito dall’albero genitore e gli eventi risalgono dai figli ai genitori secondo l’albero React.

Parameters

  • children: Qualsiasi cosa che possa essere renderizzata con React, come un pezzo di JSX (ad es. <div /> o <SomeComponent />), un Fragment (<>...</>), una stringa o un numero, oppure un array di questi.

  • domNode: Un nodo DOM, come quelli restituiti da document.getElementById(). Il nodo deve già esistere. Passare un nodo DOM diverso durante un aggiornamento farà ricreare il contenuto del portal.

  • optional key: Una stringa o un numero univoco da usare come key del portal.

Returns

createPortal restituisce un nodo React che può essere incluso nel JSX o restituito da un componente React. Se React lo incontra nell’output di renderizzazione, posizionerà i children forniti all’interno del domNode fornito.

Caveats

  • Gli eventi dai portal si propagano secondo l’albero React anziché l’albero DOM. Ad esempio, se fai clic all’interno di un portal e il portal è avvolto in <div onClick>, verrà eseguito quel gestore di eventi onClick. Se questo causa problemi, interrompi la propagazione dell’evento dall’interno del portal oppure sposta il portal stesso più in alto nell’albero React.

Usage

Renderizzare in una parte diversa del DOM

I portal permettono ai tuoi componenti di renderizzare alcuni dei loro figli in un punto diverso del DOM. Così una parte del componente può “uscire” da qualunque contenitore in cui si trovi. Ad esempio, un componente può mostrare una finestra modale o un tooltip che appare sopra e fuori dal resto della pagina.

Per creare un portal, renderizza il risultato di createPortal con del JSX e il nodo DOM in cui deve andare:

import { createPortal } from 'react-dom';

function MyComponent() {
return (
<div style={{ border: '2px solid black' }}>
<p>Questo figlio è posizionato nel div genitore.</p>
{createPortal(
<p>Questo figlio è posizionato nel body del documento.</p>,
document.body
)}
</div>
);
}

React inserirà i nodi DOM del JSX che hai passato all’interno del nodo DOM che hai fornito.

Senza un portal, il secondo <p> sarebbe posizionato all’interno del <div> genitore, ma il portal lo ha “teletrasportato” nel document.body:

import { createPortal } from 'react-dom';

export default function MyComponent() {
  return (
    <div style={{ border: '2px solid black' }}>
      <p>Questo figlio è posizionato nel div genitore.</p>
      {createPortal(
        <p>Questo figlio è posizionato nel body del documento.</p>,
        document.body
      )}
    </div>
  );
}

Nota come il secondo paragrafo appare visivamente fuori dal <div> genitore con il bordo. Se ispezioni la struttura DOM con gli strumenti per sviluppatori, vedrai che il secondo <p> è stato posizionato direttamente nel <body>:

<body>
<div id="root">
...
<div style="border: 2px solid black">
<p>Questo figlio è posizionato nel div genitore.</p>
</div>
...
</div>
<p>Questo figlio è posizionato nel body del documento.</p>
</body>

Un portal modifica solo il posizionamento fisico del nodo DOM. Per il resto, il JSX che renderizzi in un portal si comporta come un nodo figlio del componente React che lo renderizza. Ad esempio, il figlio può accedere al context fornito dall’albero genitore e gli eventi risalgono comunque dai figli ai genitori secondo l’albero React.


Renderizzare una finestra modale con un portal

Puoi usare un portal per creare una finestra modale che fluttua sopra il resto della pagina, anche se il componente che la invoca si trova dentro un contenitore con overflow: hidden o altri stili che interferiscono con la finestra.

In questo esempio, i due contenitori hanno stili che disturbano la finestra modale, ma quella renderizzata in un portal non ne è influenzata perché, nel DOM, la modale non è contenuta negli elementi JSX genitori.

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>
    </>
  );
}

Insidia

È importante assicurarsi che l’app sia accessibile quando usi i portal. Ad esempio, potresti dover gestire il focus da tastiera in modo che l’utente possa spostarlo dentro e fuori dal portal in modo naturale.

Segui le WAI-ARIA Modal Authoring Practices quando crei modali. Se usi un pacchetto della community, assicurati che sia accessibile e segua queste linee guida.


Renderizzare componenti React in markup server non React

I portal possono essere utili se la root React è solo una parte di una pagina statica o renderizzata lato server che non è costruita con React. Ad esempio, se la pagina è costruita con un framework server come Rails, puoi creare aree di interattività all’interno di aree statiche come le sidebar. Rispetto ad avere più root React separate, i portal ti permettono di trattare l’app come un unico albero React con state condiviso anche se le sue parti renderizzano in punti diversi del 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>Questa parte è renderizzata da React</p>;
}

function SidebarContent() {
  return <p>Anche questa parte è renderizzata da React!</p>;
}


Renderizzare componenti React in nodi DOM non React

Puoi anche usare un portal per gestire il contenuto di un nodo DOM gestito al di fuori di React. Ad esempio, supponiamo che tu stia integrando un widget mappa non React e voglia renderizzare contenuto React all’interno di un popup. Per farlo, dichiara una variabile di state popupContainer per memorizzare il nodo DOM in cui renderizzerai:

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

Quando crei il widget di terze parti, memorizza il nodo DOM restituito dal widget così puoi renderizzare al suo interno:

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

Questo ti permette di usare createPortal per renderizzare contenuto React in popupContainer non appena diventa disponibile:

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

Ecco un esempio completo con cui puoi sperimentare:

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>Ciao da React!</p>,
        popupContainer
      )}
    </div>
  );
}