Estoy creando una aplicación que necesita mostrar un diálogo de confirmación en algunas situaciones.
Digamos que quiero eliminar algo, luego enviaré una acción como deleteSomething(id)
para que algún reductor capture ese evento y llene el reductor de diálogo para mostrarlo.
Mi duda surge cuando se presenta este diálogo.
- ¿Cómo puede este componente despachar la acción adecuada de acuerdo con la primera acción despachada?
- ¿Debería el creador de la acción manejar esta lógica?
- ¿Podemos agregar acciones dentro del reductor?
editar:
para que quede más claro:
deleteThingA(id) => show dialog with Questions => deleteThingARemotely(id)
createThingB(id) => Show dialog with Questions => createThingBRemotely(id)
Así que estoy tratando de reutilizar el componente de diálogo. Mostrar / ocultar el diálogo no es el problema, ya que esto se puede hacer fácilmente en el reductor. Lo que intento especificar es cómo enviar la acción desde el lado derecho de acuerdo con la acción que inicia el flujo en el lado izquierdo.
javascript
modal-dialog
redux
react-redux
carlesba
fuente
fuente
Respuestas:
El enfoque que sugiero es un poco detallado, pero encontré que escala bastante bien en aplicaciones complejas. Cuando desee mostrar un modal, active una acción que describa qué modal le gustaría ver:
Envío de una acción para mostrar el modal
(Las cadenas pueden ser constantes, por supuesto; estoy usando cadenas en línea para simplificar).
Escribir un reductor para administrar el estado modal
Luego, asegúrese de tener un reductor que solo acepte estos valores:
¡Excelente! Ahora, cuando envíe una acción,
state.modal
se actualizará para incluir la información sobre la ventana modal actualmente visible.Escribir el componente modal raíz
En la raíz de su jerarquía de componentes, agregue un
<ModalRoot>
componente que esté conectado a la tienda Redux. Escucharástate.modal
y mostrará un componente modal apropiado, reenviando los accesorios desdestate.modal.modalProps
.Que hemos hecho aqui
ModalRoot
lee el actualmodalType
ymodalProps
desde elstate.modal
que está conectado, y representa un componente correspondiente comoDeletePostModal
oConfirmLogoutModal
. ¡Cada modal es un componente!Escribir componentes modales específicos
No hay reglas generales aquí. Son solo componentes React que pueden enviar acciones, leer algo del estado de la tienda y ser modales. .
Por ejemplo,
DeletePostModal
podría verse así:El
DeletePostModal
está conectado a la tienda para que pueda mostrar el título de la publicación y funciona como cualquier componente conectado: puede enviar acciones, incluidashideModal
cuando es necesario ocultarse.Extraer un componente de presentación
Sería incómodo copiar y pegar la misma lógica de diseño para cada modal "específico". Pero tienes componentes, ¿verdad? Para que puedas extraer una presentación
<Modal>
componente de que no sabe qué hacen los modales particulares, pero maneja cómo se ven.Luego, modales específicos como
DeletePostModal
pueden usarlo para renderizar:<Modal>
Depende de usted crear un conjunto de accesorios que pueda aceptar en su aplicación, pero me imagino que podría tener varios tipos de modales (por ejemplo, información modal, modal de confirmación, etc.) y varios estilos para ellos.Accesibilidad y ocultación al hacer clic fuera o tecla de escape
La última parte importante sobre los modales es que generalmente queremos ocultarlos cuando el usuario hace clic afuera o presiona Escape.
En lugar de darle consejos sobre cómo implementar esto, le sugiero que simplemente no lo implemente usted mismo. Es difícil acertar teniendo en cuenta la accesibilidad.
En cambio, le sugiero que utilice un componente modal accesible estándar como
react-modal
. Es completamente personalizable, puedes poner lo que quieras dentro de él, pero maneja la accesibilidad correctamente para que las personas ciegas puedan seguir usando tu modal.Incluso puede envolver uno
react-modal
propio<Modal>
que acepte accesorios específicos para sus aplicaciones y genere botones secundarios u otro contenido. ¡Todo son solo componentes!Otros enfoques
Hay más de una forma de hacerlo.
A algunas personas no les gusta la verbosidad de este enfoque y prefieren tener un
<Modal>
componente que puedan representar dentro de sus componentes con una técnica llamada "portales". Los portales le permiten representar un componente dentro del suyo, mientras que en realidad se representará en un lugar predeterminado en el DOM, lo cual es muy conveniente para los modales.De hecho
react-modal
, el enlace anterior ya lo hace internamente, por lo que técnicamente ni siquiera necesita renderizarlo desde la parte superior. Todavía me parece agradable desacoplar el modal que quiero mostrar del componente que lo muestra, pero también puede usarloreact-modal
directamente desde sus componentes y omitir la mayoría de lo que escribí anteriormente.Te animo a que consideres ambos enfoques, experimentes con ellos y elijas lo que creas que funciona mejor para tu aplicación y para tu equipo.
fuente
modalProps
a la acción. Sin embargo, esto viola la regla de mantener el estado serializable. ¿Cómo puedo superar este problema?Actualización : React 16.0 introdujo portales a través del
ReactDOM.createPortal
enlaceActualización : las próximas versiones de React (Fiber: probablemente 16 o 17) incluirán un método para crear portales:
ReactDOM.unstable_createPortal()
enlaceUsa 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:
Y obtienes como salida:
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 agregandocument.body
en orden de montaje, lo que significa que, a menos que juegue con ellosz-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:
Simple: aún puede usar el estado de Redux : si realmente lo desea, puede usar
connect
para elegir siDeleteConfirmationPopup
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 queDeleteConfirmationPopup
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:
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
yz-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 :)
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
fuente
<Portal>
viene? Supongo que es el portal de reacción, pero sería bueno saberlo con certeza.Aquí se pueden encontrar muchas buenas soluciones y valiosos comentarios de expertos conocidos de la comunidad de JS sobre el tema. Podría ser un indicador de que no es un problema tan trivial como puede parecer. Creo que es por eso que podría ser la fuente de dudas e incertidumbre sobre el tema.
El problema fundamental aquí es que en React solo se le permite montar el componente en su padre, que no siempre es el comportamiento deseado. Pero, ¿cómo abordar este problema?
Propongo la solución, dirigida a solucionar este problema. Una definición más detallada del problema, src y ejemplos se pueden encontrar aquí: https://github.com/fckt/react-layer-stack#rationale
Eche un vistazo a https://github.com/fckt/react-layer-stack/blob/master/README.md#real-world-usage-example para ver el ejemplo concreto que es la respuesta a su pregunta:
fuente
En mi opinión, la implementación mínima básica tiene dos requisitos. Un estado que realiza un seguimiento de si el modal está abierto o no, y un portal para representar el modal fuera del árbol de reacción estándar.
El componente ModalContainer a continuación implementa esos requisitos junto con las funciones de representación correspondientes para el modal y el desencadenante, que es responsable de ejecutar la devolución de llamada para abrir el modal.
Y aquí hay un caso de uso simple ...
Utilizo las funciones de representación, porque quiero aislar la administración de estado y la lógica repetitiva de la implementación del componente modal y del componente de activación. Esto permite que los componentes renderizados sean lo que usted quiera que sean. En su caso, supongo que el componente modal podría ser un componente conectado que recibe una función de devolución de llamada que despacha una acción asincrónica.
Si necesita enviar accesorios dinámicos al componente modal desde el componente de activación, lo que con suerte no sucede con demasiada frecuencia, le recomiendo envolver el Contenedor Modal con un componente contenedor que gestione los accesorios dinámicos en su propio estado y mejore los métodos de renderizado originales como entonces.
fuente
Envuelva el modal en un contenedor conectado y realice la operación asíncrona aquí. De esta manera, puede llegar al despacho para activar acciones y al accesorio onClose también. Para llegar
dispatch
desde los accesorios, no pase lamapDispatchToProps
función aconnect
.La aplicación donde se representa el modal y se establece su estado de visibilidad:
fuente