Contactez-nous

Portails (Portals)

Découvrez les Portails React : comment utiliser ReactDOM.createPortal pour rendre des composants (modales, tooltips) dans un autre noeud DOM tout en préservant le contexte et le flux d'événements.

S'échapper de la hiérarchie : Le besoin des portails

En React, l'architecture habituelle veut qu'un composant enfant soit rendu à l'intérieur du noeud DOM de son composant parent. Cette structure hiérarchique fonctionne bien pour la plupart des cas, mais elle peut poser des problèmes pour certains types d'éléments d'interface utilisateur qui doivent visuellement "s'échapper" de leur conteneur parent.

Pensez aux boîtes de dialogue modales, aux tooltips (infobulles), aux menus déroulants ou aux popovers. Ces éléments doivent souvent apparaître par-dessus tout le reste de l'interface, indépendamment de la position de leur composant parent dans l'arbre DOM. Si le composant parent a des styles CSS comme overflow: hidden, z-index limitant, ou des transformations complexes, cela peut couper ou masquer l'élément enfant qui est censé s'afficher par-dessus.

Pour résoudre ce problème, React propose un mécanisme appelé Portails (Portals). Un portail offre une solution de premier ordre pour rendre des composants enfants dans un noeud DOM qui existe en dehors de la hiérarchie DOM du composant parent direct, tout en conservant le lien logique et le comportement (contexte, événements) au sein de l'arbre React.

Fonctionnement et API : `ReactDOM.createPortal`

La fonctionnalité des portails est accessible via une API spécifique fournie par `react-dom` : la fonction ReactDOM.createPortal(child, container).

Cette fonction prend deux arguments :

  1. child : N'importe quel enfant React renduable. Il peut s'agir d'un élément JSX, d'un fragment, d'une chaîne de caractères, d'un nombre, etc. C'est le contenu que vous souhaitez "téléporter".
  2. container : Un noeud DOM existant dans l'arborescence DOM de la page, qui servira de conteneur pour le `child`. Ce noeud doit déjà exister dans le DOM lorsque le portail est monté. Souvent, on utilise document.body ou un élément
    dédié ajouté directement dans le fichier HTML principal (par exemple,
    ).

Typiquement, un composant utilisera `createPortal` dans sa méthode `render` (pour les composants classe) ou directement dans le retour JSX (pour les composants fonctionnels) pour indiquer que ses enfants doivent être rendus ailleurs.

Voici un exemple simple de composant `Modal` utilisant un portail pour rendre son contenu directement dans `document.body` :

import React, { useEffect } from 'react';
import ReactDOM from 'react-dom';

// Supposons qu'il existe un  dans votre index.html
const modalRoot = document.getElementById('modal-root');

function Modal({ children, onClose }) {
  // Créer un élément div pour le contenu de la modale
  const el = document.createElement('div');

  useEffect(() => {
    // Ajouter l'élément au conteneur du portail lors du montage
    modalRoot.appendChild(el);

    // Nettoyer en retirant l'élément lors du démontage
    return () => {
      modalRoot.removeChild(el);
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []); // Exécuter seulement au montage/démontage

  // Utiliser createPortal pour rendre les enfants dans l'élément 'el'
  return ReactDOM.createPortal(
    // Contenu de la modale (peut inclure un fond, bouton de fermeture, etc.)
    
{children}
, el // Le conteneur DOM cible dans lequel rendre ce JSX ); } export default Modal; // Utilisation dans un autre composant : // function App() { // const [showModal, setShowModal] = useState(false); // return ( //
// // {showModal && ( // setShowModal(false)}> //

Contenu de la Modale

//

Ceci est rendu via un portail !

//
// )} //

Reste de l'application...

//
// ); // }

Dans cet exemple, même si le composant `` est rendu à l'intérieur de `App`, son contenu JSX (`

...
`) est en fait attaché à l'élément DOM `el`, qui lui-même est ajouté à `modalRoot` (un noeud hors de la hiérarchie de `App`). L'effet `useEffect` gère la création et la suppression de cet élément `el` pour éviter les fuites de mémoire.

Propagation des événements et contexte : Le lien React persiste

Un aspect fondamental et puissant des portails est que, bien qu'ils modifient l'emplacement physique dans l'arbre DOM, ils ne modifient pas le comportement au sein de l'arbre React.

Cela signifie que :

  • La propagation des événements (event bubbling) fonctionne normalement à travers l'arbre React : Si un événement est déclenché sur un élément à l'intérieur du portail (par exemple, un clic sur le bouton "Fermer" dans notre modale), cet événement remontera (bubblera) aux ancêtres du composant dans l'arbre React, et non pas aux ancêtres dans l'arbre DOM où le portail est attaché. Ainsi, un `onClick` sur un `div` parent du composant `` dans `App` pourrait toujours être déclenché par un clic à l'intérieur de la modale (si `event.stopPropagation()` n'est pas appelé).
  • L'accès au contexte React est préservé : Un composant rendu via un portail a toujours accès à n'importe quel Contexte React fourni par un de ses ancêtres dans l'arbre React, même si ces ancêtres ne sont pas des ancêtres dans l'arbre DOM.

Ce comportement garantit que les portails s'intègrent de manière transparente dans la logique de votre application React, tout en vous donnant la flexibilité nécessaire pour le placement visuel dans le DOM.

En résumé, les Portails React, via `ReactDOM.createPortal`, sont la solution idéale lorsque vous avez besoin de rendre un composant dans un emplacement différent de l'arbre DOM (souvent pour des raisons de style CSS comme `z-index` ou `overflow`), tout en maintenant son comportement logique (événements, contexte) comme s'il était un enfant normal dans l'arbre React. C'est un outil essentiel pour construire des composants UI complexes comme les modales, les tooltips et les menus déroulants de manière propre et efficace.