Teletransportación en React.js
¿Sabías que React proporciona un componente especial y muy poderoso llamado "Portal" que puede montar a sus hijos en un lugar completamente diferente dentro de tu aplicación? Si no es así, ¡es hora de aprender algo nuevo!
La anatomía de un portal React.js
Aquí hay una descripción general rápida de cómo funcionaba el componente Portal en React.js antes.
- En algún lugar de su código, monta un elemento que alojará a los elementos secundarios del Portal.
- Entonces obtienes una referencia a este elemento
- En otro lugar de su aplicación, luego monta el componente Portal y pasa la referencia obtenida del elemento host
- Todo lo que ahora está montado dentro del Portal se montará en el host desde el primer paso.
Para comprender mejor lo que está sucediendo, he creado las siguientes visualizaciones simples para ilustrar la funcionalidad del Portal.
Sobre la base de estas imágenes, los siguientes gráficos muestran el mismo proceso, pero esta vez con ejemplos de código simplificados.
El Portal es incluso más poderoso de lo que piensas inicialmente. Incluso permite renderizar varios hijos de varios portales en el mismo elemento de destino. El orden de montaje de los portales también define el orden de los elementos en el host.
Compartir la referencia al elemento anfitrión que contendrá a todos los niños teletransportados también es muy fácil. La documentación oficial describe el uso de document.getElementById, pero también puedes usar useRef-hook de React.js. El siguiente ejemplo de código demuestra una implementación simple que muestra todo lo descrito hasta ahora.
import React, { useEffect, useRef, useState } from "react";
import ReactDOM, { createPortal } from "react-dom";
//
// Link to sandbox:
// 👉 https://codesandbox.io/s/react-16-8-0-forked-d8lm1?file=/src/index.js:0-1067
//
// A simple demonstration of the React-Portal
// w/ multiple portals for the same target.
function App() {
const ref = useRef(null);
const [isRefReady, setIsRefReady] = useState(false);
// Run one more render to actually
// show the content of our portal.
// This is necessary, as the setter
// for the 'ref' alone won't trigger
// a render.
useEffect(() => {
setIsRefReady(Boolean(ref));
}, []);
return (
<div>
<div>
{/* The 'target' aka host for our portals. */}
<header ref={ref} style={{display: "flex", gap: 8 }} />
<hr/>
</div>
<h1>
Debug
</h1>
{isRefReady && createPortal(<button>one</button>, ref.current)}
{isRefReady && createPortal(<button>two</button>, ref.current)}
<div>
{isRefReady && createPortal(<button>three</button>, ref.current)}
</div>
</div>
);
}
ReactDOM.render(<App />, document.querySelector("#root"));
Cómo el componente Portal permite una codificación más sencilla
Probablemente el caso de uso más común del portal React.js es la implementación de un componente modal. Simplemente puede definir el objetivo en la raíz del árbol de su aplicación y usar el componente Portal para montar un modal desde cualquier lugar, flotando de manera confiable sobre todo el contenido.
Otro caso de uso que implementé es el uso de un contenedor de pestañas. Como un contenedor de pestañas requiere tanto un elemento de pestaña como el contenido para representar cuando se selecciona la pestaña, simplemente defino dos hosts, uno para la fila de pestañas y el otro para el contenido seleccionado.
Luego, simplemente monto una serie de componentes, cada uno de los cuales usa un portal para el elemento de pestaña, así como el contenido seleccionado. Esto me permite ubicar la lógica empresarial donde realmente pertenece: en cada componente por separado.
Ciertamente, hay casos de uso aún más interesantes para el componente Portal. La mayor ventaja que creo que permite es el acoplamiento estrecho de la lógica empresarial en un solo componente, cuyos hijos se montan luego en diferentes lugares. Este patrón permite una gran flexibilidad y personalización, pero puede generar confusión en su base de código sobre cómo se relacionan realmente las vistas.
Consulte también la documentación oficial para conocer todos los detalles sobre los portales de React.js.