Curso react nivel medio
Introducción
En React, todos los componentes normalmente se renderizan dentro del árbol DOM del componente principal (dentro del div#root en una aplicación típica creada con Create React App). Sin embargo, hay situaciones en las que necesitas renderizar un componente fuera del árbol DOM padre, como en casos de:
- Modales (diálogos emergentes).
- Tooltips (descripciones emergentes).
- Menús desplegables.
Para estos casos, React ofrece el método createPortal.
¿Qué es createPortal?
createPortal permite renderizar un componente hijo fuera de su árbol DOM padre, en cualquier otro nodo del DOM que elijas. Esto es especialmente útil para elementos que necesitan ignorar reglas de CSS de su contenedor (como overflow: hidden).
Sintaxis de createPortal
1ReactDOM.createPortal( 2 children, // El contenido que se renderizará 3 container // El nodo del DOM donde se renderizará 4);
children: Lo que quieres renderizar (puede ser un componente, un elemento JSX o incluso texto).container: El nodo del DOM donde se montará el contenido.
Ejemplo Básico: Modal con createPortal
Paso 1: Configurar el HTML
Asegúrate de que tu archivo index.html tenga un contenedor adicional para el portal.
1<div id="root"></div> 2<div id="modal-root"></div>
Paso 2: Crear el Componente del Modal
1import React from "react"; 2import ReactDOM from "react-dom"; 3 4function Modal({ children, onClose }) { 5 return ReactDOM.createPortal( 6 <div style={modalStyles}> 7 <div style={modalContentStyles}> 8 {children} 9 <button onClick={onClose}>Cerrar</button> 10 </div> 11 </div>, 12 document.getElementById("modal-root") // El nodo destino del portal 13 ); 14} 15 16const modalStyles = { 17 position: "fixed", 18 top: 0, 19 left: 0, 20 width: "100%", 21 height: "100%", 22 backgroundColor: "rgba(0, 0, 0, 0.5)", 23 display: "flex", 24 justifyContent: "center", 25 alignItems: "center", 26}; 27 28const modalContentStyles = { 29 backgroundColor: "white", 30 padding: "20px", 31 borderRadius: "5px", 32}; 33 34export default Modal;
Paso 3: Usar el Componente Modal
1import React, { useState } from "react"; 2import Modal from "./Modal"; 3 4function App() { 5 const [mostrarModal, setMostrarModal] = useState(false); 6 7 const abrirModal = () => setMostrarModal(true); 8 const cerrarModal = () => setMostrarModal(false); 9 10 return ( 11 <div> 12 <h1>Ejemplo con Portales</h1> 13 <button onClick={abrirModal}>Abrir Modal</button> 14 {mostrarModal && ( 15 <Modal onClose={cerrarModal}> 16 <h2>Este es un modal</h2> 17 <p>Puedes hacer clic fuera para cerrarlo.</p> 18 </Modal> 19 )} 20 </div> 21 ); 22} 23 24export default App;
Explicación
- HTML: Creamos un contenedor aparte (
#modal-root) para los portales. - Componente Modal:
- Usamos
ReactDOM.createPortalpara renderizar el contenido del modal en#modal-root.
- Usamos
- Estado: Controlamos si el modal está visible con un estado (
mostrarModal). - Botón de Cierre: Llamamos a
cerrarModalcuando el usuario cierra el modal.
¿Por Qué Usar createPortal?
1. Ignorar Estilos del Contenedor Padre
Si el contenedor tiene overflow: hidden o z-index bajo, los elementos renderizados fuera del árbol del DOM principal no estarán afectados.
2. Organización del DOM
Mantener modales, tooltips o elementos emergentes en un contenedor separado (#modal-root) mejora la organización y el rendimiento.
Ejemplo Avanzado: Tooltip con createPortal
Tooltip Componente
1import React from "react"; 2import ReactDOM from "react-dom"; 3 4function Tooltip({ texto, posicion, children }) { 5 const tooltipStyles = { 6 position: "absolute", 7 backgroundColor: "black", 8 color: "white", 9 padding: "5px", 10 borderRadius: "3px", 11 ...posicion, 12 }; 13 14 return ReactDOM.createPortal( 15 <div style={tooltipStyles}>{texto}</div>, 16 document.body // Renderizamos directamente en el body 17 ); 18} 19 20export default Tooltip;
Usar el Tooltip
1import React, { useState } from "react"; 2import Tooltip from "./Tooltip"; 3 4function App() { 5 const [mostrarTooltip, setMostrarTooltip] = useState(false); 6 const [posicion, setPosicion] = useState({}); 7 8 const manejarMouseEnter = (e) => { 9 const { top, left, width } = e.target.getBoundingClientRect(); 10 setPosicion({ top: top - 30, left: left + width / 2 }); 11 setMostrarTooltip(true); 12 }; 13 14 const manejarMouseLeave = () => { 15 setMostrarTooltip(false); 16 }; 17 18 return ( 19 <div style={{ marginTop: "100px", textAlign: "center" }}> 20 <button 21 onMouseEnter={manejarMouseEnter} 22 onMouseLeave={manejarMouseLeave} 23 > 24 Pasa el mouse aquí 25 </button> 26 {mostrarTooltip && ( 27 <Tooltip texto="¡Hola! Este es un tooltip" posicion={posicion} /> 28 )} 29 </div> 30 ); 31} 32 33export default App;
Explicación
- Tooltip:
- Usamos
createPortalpara renderizar el tooltip directamente en elbody. - El tooltip se posiciona dinámicamente usando
getBoundingClientRectdel elemento.
- Usamos
- Estado: Controlamos la visibilidad del tooltip con
mostrarTooltip. - Posicionamiento: Calculamos la posición del tooltip al pasar el mouse sobre el botón.
Consideraciones de createPortal
-
Mantenimiento del Contexto
Aunque el portal renderiza el componente fuera del árbol DOM padre, este mantiene el contexto React (comouseContext). -
Manejo del Foco
Si usas portales para modales, asegúrate de manejar el foco adecuadamente para accesibilidad (por ejemplo, enfocar el primer elemento del modal). -
Cierre Automático
Asegúrate de implementar un cierre cuando el usuario haga clic fuera del componente (modal o tooltip).
Conclusión
createPortal es una herramienta poderosa que te permite crear elementos que necesitan renderizarse fuera del árbol DOM principal. Esto es especialmente útil para casos como modales, tooltips o menús desplegables.