Actualización : React 16.0 introdujo portales a través del ReactDOM.createPortal
enlace
Actualización : las próximas versiones de React (Fiber: probablemente 16 o 17) incluirán un método para crear portales: ReactDOM.unstable_createPortal()
enlace
Usa portales
La primera parte de la respuesta de Dan Abramov está bien, pero involucra muchas repeticiones. Como él dijo, también puedes usar portales. Ampliaré un poco esa idea.
La ventaja de un portal es que la ventana emergente y el botón permanecen muy cerca del árbol React, con una comunicación padre / hijo muy simple usando accesorios: puede manejar fácilmente acciones asíncronas con portales, o dejar que el padre personalice el portal.
¿Qué es un portal?
Un portal le permite renderizar directamente dentro de document.body
un elemento que está profundamente anidado en su árbol React.
La idea es que, por ejemplo, conviertas en cuerpo el siguiente árbol React:
<div className="layout">
<div className="outside-portal">
<Portal>
<div className="inside-portal">
PortalContent
</div>
</Portal>
</div>
</div>
Y obtienes como salida:
<body>
<div class="layout">
<div class="outside-portal">
</div>
</div>
<div class="inside-portal">
PortalContent
</div>
</body>
El inside-portal
nodo ha sido traducido dentro<body>
, en lugar de su lugar normal, profundamente anidado.
Cuando usar un portal
Un portal es particularmente útil para mostrar elementos que deben ir por encima de sus componentes React existentes: ventanas emergentes, menús desplegables, sugerencias, puntos de acceso
¿Por qué usar un portal?
Ya no hay problemas con el índice z : un portal le permite renderizar <body>
. Si desea mostrar una ventana emergente o desplegable, esta es una muy buena idea si no quiere tener que luchar contra los problemas del índice z. Los elementos del portal se agregan document.body
en orden de montaje, lo que significa que, a menos que juegue con ellos z-index
, el comportamiento predeterminado será apilar los portales uno encima del otro, en orden de montaje. En la práctica, significa que puede abrir de manera segura una ventana emergente desde el interior de otra ventana emergente, y asegúrese de que la segunda ventana emergente se mostrará encima de la primera, sin tener que pensar siquiera enz-index
.
En la práctica
Lo más simple: use el estado de Reacción local: si cree que, para una simple ventana emergente de confirmación de eliminación, no vale la pena tener el repetitivo de Redux, entonces puede usar un portal y simplifica enormemente su código. Para tal caso de uso, donde la interacción es muy local y en realidad es un detalle de implementación, ¿realmente le importa la recarga en caliente, el viaje en el tiempo, el registro de acciones y todos los beneficios que Redux le brinda? Personalmente, no lo hago y uso el estado local en este caso. El código se vuelve tan simple como:
class DeleteButton extends React.Component {
static propTypes = {
onDelete: PropTypes.func.isRequired,
};
state = { confirmationPopup: false };
open = () => {
this.setState({ confirmationPopup: true });
};
close = () => {
this.setState({ confirmationPopup: false });
};
render() {
return (
<div className="delete-button">
<div onClick={() => this.open()}>Delete</div>
{this.state.confirmationPopup && (
<Portal>
<DeleteConfirmationPopup
onCancel={() => this.close()}
onConfirm={() => {
this.close();
this.props.onDelete();
}}
/>
</Portal>
)}
</div>
);
}
}
Simple: aún puede usar el estado de Redux : si realmente lo desea, puede usar connect
para elegir si DeleteConfirmationPopup
se muestra o no. Como el portal permanece profundamente anidado en su árbol React, es muy sencillo personalizar el comportamiento de este portal porque su padre puede pasar accesorios al portal. Si no usa portales, generalmente debe mostrar sus ventanas emergentes en la parte superior de su árbol React paraz-index
motivos son , y generalmente tiene que pensar en cosas como "¿cómo personalizo el DeleteConfirmationPopup genérico que construí de acuerdo con el caso de uso? ". Y, por lo general, encontrará soluciones bastante hacky para este problema, como enviar una acción que contiene acciones de confirmación / cancelación anidadas, una clave de paquete de traducción o, lo que es peor, una función de representación (o algo más no serializable). No tiene que hacer eso con los portales, y solo puede pasar accesorios regulares, ya que DeleteConfirmationPopup
es solo un hijo delDeleteButton
Conclusión
Los portales son muy útiles para simplificar su código. No podría prescindir de ellos nunca más.
Tenga en cuenta que las implementaciones de portal también pueden ayudarlo con otras características útiles como:
- Accesibilidad
- Atajos de espacio para cerrar el portal
- Manejar el clic externo (cerrar el portal o no)
- Manejar clic en el enlace (cerrar portal o no)
- Contexto de reacción disponible en el árbol del portal
react-portal o react-modal son buenos para ventanas emergentes, modales y superposiciones que deberían estar en pantalla completa, generalmente centradas en el medio de la pantalla.
react-tether es desconocido para la mayoría de los desarrolladores de React, sin embargo, es una de las herramientas más útiles que puedes encontrar. Tether le permite crear portales, pero posicionará automáticamente el portal, en relación con un objetivo determinado. Esto es perfecto para información sobre herramientas, menús desplegables, zonas interactivas, cajas de ayuda ... Si alguna vez ha tenido algún problema con la posición absolute
/ relative
y z-index
, o si su menú desplegable sale de su ventana gráfica, Tether resolverá todo eso por usted.
Puede, por ejemplo, implementar fácilmente zonas activas de incorporación, que se expanden a una información sobre herramientas una vez que se hace clic en ellas:
Código de producción real aquí. No puede ser más simple :)
<MenuHotspots.contacts>
<ContactButton/>
</MenuHotspots.contacts>
Editar : acaba de descubrir react-gateway que permite representar portales en el nodo de su elección (no necesariamente el cuerpo)
Editar : parece que react-popper puede ser una alternativa decente a react-tether. PopperJS es una biblioteca que solo calcula una posición apropiada para un elemento, sin tocar el DOM directamente, lo que permite al usuario elegir dónde y cuándo quiere colocar el nodo DOM, mientras que Tether se agrega directamente al cuerpo.
Editar : también hay react-slot-fill, que es interesante y puede ayudar a resolver problemas similares al permitir representar un elemento en un espacio de elemento reservado que coloque en cualquier lugar que desee en su árbol