React Portals. Una introducción
Cómo emplear los React Portals para saltarte algunas de las restricciones que impone el DOM
Hace poco estuve trabajando en una aplicación que empleaba los React Portals y me pareció una característica muy interesante que nos ofrece React para permitirnos de forma nativa renderear componentes en un nodo del DOM que existen por fuera de la jerarquía del componente padre.
Dicho de otro modo, los React Portals son una API que nos permite renderear componentes “saltándonos” la jerarquía del DOM. Según la documentación oficial los portales resultan útiles cuando un componente padre tiene una propiedad del estilo z-index
o overflow: hidden
pero su hijo tiene que “romper” el contenedor, como por ejemplo en el caso de los tooltips, ventanas modales o menús flotantes.
Puesto que de primeras todo esto puede sonar bastante abstracto voy a mostraros dos casos de uso para que podáis ver su funcionamiento y que así entenderlos os resulte más sencillo.
¡Vamos a ello!
Creando una ventana modal con React Portals
El primer caso va a ser bastante sencillo y es el más típico a la hora de entender la manera en que funcionan los React Portals. Lo que haremos será desarrollar una aplicación muy sencilla que nos permita mostrar una ventana modal pulsando un botón.
Para ello, partiremos de un proyecto creado desde cero mediante la herramienta npx
.
Lo primero que haremos será añadir a nuestro archivo index.html
el elemento que contendrá a las ventanas modales de nuestra habitación y hacia el que posteriormente crearemos el portal:

Como veis, el elemento modalContainer
se encuentra al mismo nivel que el elemento root
(que es por otro lado donde se montará nuestra aplicación). La magia de los React Portals nos permitirá renderear componentes en el elemento del DOM modalContainer
saltándonos la jerarquía de componentes de React que creemos en nuestra aplicación.
Para ello, el siguiente paso será crear nuestro componente Modal
, que tendrá el siguiente aspecto:

Este componente recibe 4 propiedades pero lo que realmente nos interesa es que es aquí donde aparece el uso de ReactDOM.createPortal
. Esta función recibe dos argumentos:
- La estructura a renderear como si del propio retorno de un componente funcional (o del método
render
en un componente de clase) se tratase. - El elemento del DOM donde queremos renderearlo. Es por ello que en la línea 5 estamos recurriendo a la función
document.querySelector
para seleccionar nuestro<div id="modalContainer"></div>
que añadimos en el archivoindex.html
.
Hecho esto, tan sólo quedará añadir nuestro componente dentro del archivo App.js
y la “magia” de los React Portals hará el resto:

Lo realmente interesante es que a nivel de React la estructura de componentes es la que vemos representada en nuestros archivos. Sin embargo, al hacer click sobre el botón Open Modal
la estructura del DOM que obtenemos es la siguiente:

Repositorio
En el caso de que queráis ver en más detalle el código podéis ver el siguiente repositorio:
Propagación de eventos
Otra de las cosas curiosas al trabajar con los React Portals es la forma en que se propagan los eventos, ya que aunque el contenido del portal se pinte en otra parte del DOM el componente a nivel interno se sigue comportando como si de un hijo del componente padre se tratase.
Dicho de otro modo y tomando como referencia las palabras de la propia documentación de React:
Un evento activado desde adentro de un portal se propagará a los ancestros en el árbol de React, incluso si esos elementos no son ancestros en el árbol DOM.
Veámoslo modificando un poco el ejemplo anterior:

El componente <Child>
, que pintamos dentro de nuestro componente Modal
posee un botón capaz de recibir clicks (línea 6) pero, además, hemos añadido un onClick
en la línea 17 de modo que cada click dentro del componente App
se incremente el contador.
Lo interesante es que, pese a que hagamos click en el botón pintado dentro de la modal (el cual es pintado en el elemento del DOM modalContainer
), el componente App
sigue recibiendo los clicks puesto que React mantiene la jerarquía de los componentes.
Agujeros de gusano y React Portals
Veamos ahora un ejemplo algo más interesante: crear un agujero de gusano con React. Según la teoría:
En física, un agujero de gusano, también conocido como puente de Einstein-Rosen, es una hipotética característica topológica de un espacio-tiempo, descrita en las ecuaciones de la relatividad general, que esencialmente consiste en un atajo a través del espacio y el tiempo.
Para simular el comportamiento de un agujero de gusano empleando los portales de React crearemos un nuevo proyecto empleando npx
e instalaremos la librería react-draggable
que nos permitirá mover nuestra nave espacial por el espacio de nuestra aplicación.
yarn add react-draggable
Nota. 🤗 Probablemente no es la mejor librería para implementar el comportamiento drag pero por no complicar el ejemplo será la que emplearemos.
La idea del proyecto será tener dos “portales” que nos permitan conectar una nave espacial entre el espacio A (donde se encuentra el root
de nuestra aplicación de React) y el espacio B.
Para comenzar, crearemos el componente que representará nuestra nave espacial:

y añadiremos a nuestro archivo index.html
el elemento que contendrá el espacio B de nuestra aplicación:

Y ahora es donde comienza la parte interesante. Queremos poder desplazar nuestra nave por el espacio A y arrastrarla hasta una puerta que la traslade hasta el espacio B.
Para ello, dentro de nuestro componente principal escribiremos lo siguiente:

Lo más importante de este componente es lo siguiente:
- En la línea 7 obtenemos el elemento
wormhole-b-container
que representa el espacio B. - En la línea 18 empleamos el hook
useState
para almacenar en qué espacio se encuentra nuestra nave. - De cara a poder comprobar si la nave ha llegado al agujero de gusano, empleamos el hook
useRef
para obtener una referencia al elementowormhole-a
que representa dicho agujero. - La función
checkInsideWormholeA
se encarga de comprobar si la nave ha llegado al agujero de gusano obteniendo elboundingRect
de ella y del elemento que representa el agujero de gusano. - En la línea 38 estamos añadiendo mediante un portal el agujero de gusano del espacio B para que la nave pueda volver más adelante.
De este modo, lo que obtenemos al ejecutar el proyecto en este punto es lo siguiente:

Es decir, la nave llega al agujero y desaparece. ¿Cómo conseguimos que aparezca en el espacio B? Del siguiente modo:

Como podéis ver, en la línea 49 hemos creado otro portal mediante ReactDom.createPortal
para insertar la nave en el caso de que la nave haya salido del espacio A.
Por otra parte añadiremos la función checkInsideWormholeB
con el fin de comprobar si la nave se encuentra en el agujero de gusano del espacio B para poder volver al A.
De este modo ya podemos viajar entre los dos espacios sin problemas:

Repositorio
Si queréis profundizar en este ejemplo os dejo el repositorio:
Conclusiones y referencias
Como habéis podido comprobar, la API ReactDOM.createPortal
abre un mundo de posibilidades a la hora de poder manipular la estructura del DOM. Estos dos ejemplos son solo la punta del iceberg de algunas de las cosas que se pueden llegar a lograr gracias a esta característica; por ejemplo, empleando los React Portals podemos incluso mover elementos entre ventanas:
Además, podéis profundizar más en la documentación oficial y ver más ejemplos como el de este artículo.
Espero que este artículo os haya gustado y os anime a profundizar más en esta funcionalidad de React que a veces pasa desapercibida hasta que llega el momento en que nos hablan de ella.
¿Quieres recibir más artículos como este?
Si te ha gustado este artículo te animo a que te suscribas a la newsletter que envío cada domingo con publicaciones similares a esta y más contenido recomendado: 👇👇👇