Digamos que necesitamos convertir un elemento secundario en una aplicación React. ¿Fácil verdad? Ese elemento secundario se monta en el elemento DOM más cercano y, como resultado, se representa dentro de él.
render() {
return (
<div>
// Child to render inside of the div
</div>
);
}
¡Pero! ¿Qué sucede si queremos representar a ese niño fuera del div en otro lugar? Eso podría ser complicado porque rompe la convención de que un componente debe representarse como un elemento nuevo y seguir una jerarquía padre-hijo. El padre quiere ir adonde va su hijo.
Ahí es donde entran los React Portals. Proporcionan una forma de renderizar elementos fuera de la jerarquía DOM para que los elementos sean un poco más portátiles. Puede que no sea una analogía perfecta, pero los portales son como las tuberías de Mario Bros. que te transportan del flujo normal del juego a una región diferente.
¿Lo bueno de los portales? Aunque desencadenan sus propios eventos que son independientes del elemento principal del elemento secundario, el elemento principal sigue escuchando esos eventos, lo que puede ser útil para pasar eventos a través de una aplicación.
Vamos a crear un Portal juntos en esta publicación y luego convertirlo en un componente reutilizable. ¡Vamos!
El ejemplo que estamos construyendo
Aquí hay un ejemplo relativamente simple de un Portal en acción:
Vea el portal Pen React de Kingsley Silas Chijioke (@kinsomicrote) en CodePen.
Alternar la visibilidad de un elemento no es nada nuevo. Pero, si observa el código detenidamente, notará que el elemento de salida está controlado por el botón, aunque no es un descendiente directo de él. De hecho, si compara el código fuente con la salida renderizada en DevTools, verá la relación:
Entonces, el padre del elemento de salida realmente escucha el evento de clic del botón y permite que el hijo se inserte aunque él y el botón sean hermanos separados en el DOM. Desglosemos los pasos para crear este elemento Portal alternado para ver cómo funciona todo.
Paso 1: Crear el elemento Portal
La primera línea de una aplicación React le indicará que un elemento de la aplicación se representa en la raíz del documento mediante ReactDOM. Como esto;
ReactDOM.render(<App />, document.getElementById("root"));
Necesitamos colocar el elemento App en un archivo HTML para ejecutarlo:
<div id="App"></div>
El mismo tipo de cosas con Portals. Lo primero para crear un Portal es crear un nuevo elemento div en el archivo HTML.
<div id="portal"></div>
Este div servirá como nuestro objetivo. estamos usando #portal
como el ID, pero no tiene que ser eso. Cualquier componente que se represente dentro de este div de destino mantendrá el contexto de React. Necesitamos almacenar el div como el valor de una variable para que podamos usar el componente Portal que crearemos:
const portalRoot = document.getElementById("portal");
Se parece mucho al método para ejecutar el elemento App, ¿verdad?
Paso 2: crear un componente de portal
A continuación, configuremos el Portal como un componente:
class Portal extends React.Component {
constructor() {
super();
// 1: Create a new div that wraps the component
this.el = document.createElement("div");
}
// 2: Append the element to the DOM when it mounts
componentDidMount = () => {
portalRoot.appendChild(this.el);
};
// 3: Remove the element when it unmounts
componentWillUnmount = () => {
portalRoot.removeChild(this.el);
};
render() {
// 4: Render the element's children in a Portal
const { children } = this.props;
return ReactDOM.createPortal(children, this.el);
}
}
Demos un paso atrás y echemos un vistazo a lo que está sucediendo aquí.
Creamos un nuevo elemento div en el constructor y lo establecemos como un valor para this.el
. Cuando se monta el componente Portal, this.el
se agrega como elemento secundario a ese div en el archivo HTML donde lo agregamos. Eso es <div id="portal"></div>
línea en nuestro caso.
El árbol DOM se verá así.
<div> // Portal, which is also portalRoot
<div> // this.el
</div>
</div>
Si eres nuevo en React y te confunde el concepto de montar y desmontar un elemento, Jake Trent tiene una buena explicación. TL; DR: el montaje es el momento en que el elemento se inserta en el DOM.
Cuando el componente se desmonta, queremos eliminar al niño para evitar cualquier pérdida de memoria. Importaremos este componente Portal a otro componente donde se use, que es el div que contiene el encabezado y el botón en nuestro ejemplo. Al hacerlo, pasaremos los elementos secundarios del componente Portal junto con él. Por eso tenemos this.props.children
.
Paso 3: Uso del Portal
Para representar los elementos secundarios del componente Portal, hacemos uso de ReactDOM.createPortal()
. Este es un método ReactDOM especial que acepta los niños y el elemento que creamos. Para ver cómo funciona el Portal, hagamos uso de él en nuestro componente de aplicación.
Pero, antes de hacer eso, cubramos los conceptos básicos de cómo queremos que funcione la aplicación. Cuando se carga la aplicación, queremos mostrar un texto y un botón; luego podemos alternar el botón para mostrar u ocultar el componente Portal.
class App extends React.Component {
// The initial toggle state is false so the Portal element is out of view
state = {
on: false
};
toggle = () => {
// Create a new "on" state to mount the Portal component via the button
this.setState({
on: !this.state.on
});
};
// Now, let's render the components
render() {
const { on } = this.state;
return (
// The div where that uses the Portal component child
<div>
<header>
<h1>Welcome to React</h1>
</header>
<React.Fragment>
// The button that toggles the Portal component state
// The Portal parent is listening for the event
<button onClick={this.toggle}>Toggle Portal</button>
// Mount or unmount the Portal on button click
<Portal>
{
on ?
<h1>This is a portal!</h1>
: null
}
</Portal>
</React.Fragment>
</div>
);
}
}
Dado que queremos activar y desactivar el Portal, debemos utilizar el estado del componente para administrar la alternancia. Eso es básicamente un método para establecer un estado de on
a cualquiera true
o false
en el evento de clic. El portal se renderiza cuando on
es verdad; de lo contrario no representamos nada.
Así es como se ve el DOM cuando el on
el estado se establece en true
.
Cuándo on
es false
, el componente Portal no se representa en la raíz, por lo que el DOM tiene este aspecto.
Más casos de uso
Los modales son un candidato perfecto para los portales. De hecho, los documentos de React lo usan como ejemplo principal de cómo funcionan los portales:
Vea el ejemplo de pluma: portales de Dan Abramov (@gaearon) en CodePen.
Es el mismo concepto, donde se crea un componente Portal y se usa un estado para agregar sus elementos secundarios al componente Modal.
Incluso podemos insertar datos de una fuente externa en un modal. En este ejemplo, el componente de la aplicación enumera los usuarios obtenidos de una API mediante axios.
Vea Pen React Portal 3 de Kingsley Silas Chijioke (@kinsomicrote) en CodePen.
¿Qué hay de la información sobre herramientas? David Gilberton tiene una buena demostración:
Vea el Tooptip del portal Pen React de David Gilbertson (@davidgilbertson) en CodePen.
J Scott Smith muestra cómo se pueden usar los portales para escapar del posicionamiento:
Tiene otro ejemplo ingenioso que demuestra la inserción de elementos y el estado de gestión:
Resumen
¡Eso es un envoltorio! Con suerte, esto le brinda una base sólida de comprensión de los portales en cuanto a qué son, qué hacen y cómo usarlos en una aplicación React. El concepto puede parecer trivial, pero tener la capacidad de mover elementos fuera de la jerarquía DOM es una forma práctica de hacer que los componentes sean un poco más extensibles y reutilizables… todo lo cual apunta a los beneficios principales de usar React en primer lugar.
Más información
- Documentación de los portales React
- Notas de la versión de React v16: la versión en la que se introdujeron los portales
- Ejemplos de CodePen: una búsqueda de React Portals