React Portals. Lanzar un popup que pueda acceder al estado del componente
Los React Portals permiten crear componentes que accedan al estado del componente padre pese a estar montados sobre elementos del DOM fuera de la jerarquía
Para aquellos que no conozcáis los React Portals, esta API de React nos permite renderizar componentes hijos sobre elementos del DOM que se encuentren fuera de la jerarquía del elemento podre.
Tal y como apunta la documentación de React esto puede resultar muy útil en casos donde interactúen elementos que usan propiedades CSS como z-index
u overflow
, ya que los elementos hijos del DOM se ven restringidos por las definiciones de los padres. Gracias a los React Portals podremos renderizar en el DOM un componente fuera del padre y así conseguir saltarnos estas restricciones.
En el artículo de hoy quiero mostraros cómo podemos emplear los React Portals para crear popups mediante la instrucción window.open
que puedan acceder al estado del componente padre tal y cómo por ejemplo hacen las típicas ventanas de chat de GMail.
¡Vamos allá!
¿Qué queremos hacer?
La aplicación en la que se basa este artículo es muy sencilla. Definiremos un contador mediante el hook useState
que podrá ser actualizado tanto desde el componente padre como desde un popup que lanzaremos empleando los React Portals.

Definición del componente padre

La definición del componente padre como veis es muy sencilla. Definimos por medio del hook useState
dos estados:
- El estado del contador junto con una función
incrementCounter
que permite incrementar el valor del contador. - El estado del popup para determinar si éste se encuentra abierto o cerrado.
Además, de la línea 19 a la 27 crearemos el layout que permite mostrar el estado del contador así como los botones que permiten incrementar este contador y modificar el estado del popup entre abierto o cerrado.
Finalmente, en la línea 29 emplearemos el componente WindowPortal
que crearemos en el siguiente paso para mostrar esta misma información: estado del contador y los botones para incrementar el contador y cambiar el estado de visibilidad del popup.
Hasta aquí fácil, ¿verdad? Veamos ahora cómo es el componente WindowPortal
.
El componente WindowPortal
Pasemos ahora a lo interesante. Para crear portales en una aplicación desarrollada con React es necesario importar ReactDOM
desde el paquete react-dom
e invocar su método createPortal
. Este método recibe dos argumentos:
- El primero
children
, es cualquier componente de React que pueda ser renderizado. - El segundo,
element
, es el elemento del DOM sobre el cual queremos montar la propiedadchildren
anterior.
Un componente que quiera usar Portals lo que deberá hacer es devolver el valor de esta invocación:
function myComponent() {
...
return ReactDOM.createPortal(children, domElement);
}
En el caso de nuestro componente WindowPortal
, lo que haremos será crear un popup tras el primer renderizado del componente por medio de la instrucción window.open
y añadirle un elemento div
sobre el cual montaremos la propiedad children
recibida:

En la línea 5 lo que haremos será almacenar en el estado el elemento del DOM que crearemos para montar sobre él la propiedad children
gracias a Portals.
En la línea 6 crearemos una referencia por medio del hook useRef
que albergará el elemento devuelto por la instrucción window.open
.
En la línea 8 declararemos un efecto sin dependencias, por lo que sólo se ejecutará una única vez tras el primer render. Dentro de este efecto realizaremos lo siguiente:
- Abriremos un popup cuya referencia almacenaremos en
externalWindow
. - Crearemos un elemento
div
que añadiremos a la ventana recién creada (líneas 14 y 15). - Almacenaremos en el estado este nuevo elemento para forzar un render del componente (línea 16).
- Dentro del efecto devolveremos una función que nos permita cerrar la ventana una vez que el componente sea desmontado.
¿Y qué devuelve nuestro componente WindowPortal
? Para que todo funcione correctamente como veremos en el siguiente punto, el componente devolverá:
null
en el caso de que todavía no se haya creado eldiv
sobre el que se montará la propiedadchildren
recibida,- el resultado de
ReactDom.createPortal(children, windowPortalElement)
una vez que ya tengamos un elemento en el DOM sobre el que montar los hijos.
Con esto ya tendremos nuestro portal funcionando y podremos ver cómo podemos incrementar el contador tanto desde la ventana principal como desde el popup.
Podéis ver en funcionamiento esta mini aplicación bajando y ejecutando el siguiente repositorio:
😅 Problema con el que nos podemos encontrar
Pese a que este ejemplo funciona sin problemas hay algo que debemos saber. Si nosotros definiéramos por ejemplo un div
dentro del archivo index.html
para albergar el contenido del popup:
<div id="windowPortal"></div>
Y en el componente WindowPortal
empleáramos el siguiente código:

Nos encontraríamos con que los botones de la modal no funcionarán hasta que no presionemos primero los del padre:
Esto se encuentra realizado con la siguiente issue
de React:
Que viene a decir lo siguiente. Al montar primero por medio de ReactDOM.createPortal
los children
sobre el elemento del DOM presente en el documento padre y posteriormente llevárnoslo al popup por medio de la instrucción appendChild
(línea 16), el navegador pierde los event listeners debido al cambio de documento, y estos no se vuelven a establecer hasta que se fuerza un nuevo render (pulsando por ejemplo el botón incrementar en el padre).
Así que cuidado con los eventos cuando empleéis los React Portals ya que en función de cómo defináis los elementos y la forma en que se asignan al portal pueden perderse sus handlers.
Conclusiones
Sobre los React Portals ya hice una introducción en el siguiente artículo:
Y como habéis podido ver, son un excelente recurso para trabajar con ventanas modales o incluso con popups tal y como he descrito en el artículo.
Espero que os haya resultado interesante este artículo y que os haya dado nuevas ideas a la hora de implementar funcionalidades de vuestras aplicaciones.
¿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: 👇👇👇