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

  1. HTML: Creamos un contenedor aparte (#modal-root) para los portales.
  2. Componente Modal:
    • Usamos ReactDOM.createPortal para renderizar el contenido del modal en #modal-root.
  3. Estado: Controlamos si el modal está visible con un estado (mostrarModal).
  4. Botón de Cierre: Llamamos a cerrarModal cuando 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

  1. Tooltip:
    • Usamos createPortal para renderizar el tooltip directamente en el body.
    • El tooltip se posiciona dinámicamente usando getBoundingClientRect del elemento.
  2. Estado: Controlamos la visibilidad del tooltip con mostrarTooltip.
  3. Posicionamiento: Calculamos la posición del tooltip al pasar el mouse sobre el botón.

Consideraciones de createPortal

  1. Mantenimiento del Contexto
    Aunque el portal renderiza el componente fuera del árbol DOM padre, este mantiene el contexto React (como useContext).

  2. 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).

  3. 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.