Tengo una acción que actualiza el estado de notificación de mi aplicación. Por lo general, esta notificación será un error o información de algún tipo. Necesito luego enviar otra acción después de 5 segundos que devolverá el estado de notificación al inicial, por lo que no hay notificación. La razón principal detrás de esto es proporcionar funcionalidad donde las notificaciones desaparecen automáticamente después de 5 segundos.
No tuve suerte con el uso setTimeout
y la devolución de otra acción y no puedo encontrar cómo se hace esto en línea. Entonces cualquier consejo es bienvenido.
redux-saga
respuesta basada si desea algo mejor que thunks. Respuesta tardía, por lo que debe desplazarse mucho tiempo antes de que aparezca :) no significa que no valga la pena leerlo. Aquí hay un atajo: stackoverflow.com/a/38574266/82609Respuestas:
No caigas en la trampa de pensar que una biblioteca debería prescribir cómo hacer todo . Si desea hacer algo con un tiempo de espera en JavaScript, debe usarlo
setTimeout
. No hay ninguna razón por la cual las acciones de Redux sean diferentes.Redux no ofrecen algunas opciones alternativas para tratar con cosas asíncrono, pero sólo se debe utilizar aquellas en las que se da cuenta que se está repitiendo demasiado código. A menos que tenga este problema, use lo que ofrece el idioma y busque la solución más simple.
Escribir código asíncrono en línea
Esta es, con mucho, la forma más simple. Y no hay nada específico para Redux aquí.
Del mismo modo, desde el interior de un componente conectado:
La única diferencia es que, en un componente conectado, generalmente no tiene acceso a la tienda en sí, sino que se
dispatch()
inyecta a los creadores de acciones específicas o a cualquiera de ellos . Sin embargo, esto no hace ninguna diferencia para nosotros.Si no le gusta hacer errores tipográficos al despachar las mismas acciones desde diferentes componentes, es posible que desee extraer creadores de acciones en lugar de enviar objetos de acción en línea:
O, si los ha vinculado previamente con
connect()
:Hasta ahora no hemos utilizado ningún middleware u otro concepto avanzado.
Extrayendo Async Action Creator
El enfoque anterior funciona bien en casos simples, pero es posible que tenga algunos problemas:
HIDE_NOTIFICATION
, ocultando erróneamente la segunda notificación antes de que transcurra el tiempo de espera.Para resolver estos problemas, necesitaría extraer una función que centralice la lógica del tiempo de espera y distribuya esas dos acciones. Podría verse así:
Ahora los componentes pueden usarse
showNotificationWithTimeout
sin duplicar esta lógica o tener condiciones de carrera con notificaciones diferentes:¿Por qué
showNotificationWithTimeout()
aceptadispatch
como primer argumento? Porque necesita enviar acciones a la tienda. Normalmente, un componente tiene acceso,dispatch
pero dado que queremos que una función externa tome control sobre el despacho, debemos darle el control sobre el despacho.Si tenía una tienda Singleton exportada desde algún módulo, simplemente podría importarla y
dispatch
directamente en ella:Esto parece más simple pero no recomendamos este enfoque . La razón principal por la que no nos gusta es porque obliga a la tienda a ser un singleton . Esto hace que sea muy difícil implementar la representación del servidor . En el servidor, querrá que cada solicitud tenga su propia tienda, para que diferentes usuarios obtengan diferentes datos precargados.
Una tienda Singleton también hace que las pruebas sean más difíciles. Ya no puede burlarse de una tienda cuando prueba creadores de acciones porque hacen referencia a una tienda real específica exportada desde un módulo específico. Ni siquiera puede restablecer su estado desde el exterior.
Entonces, aunque técnicamente puede exportar una tienda singleton desde un módulo, lo desaconsejamos. No haga esto a menos que esté seguro de que su aplicación nunca agregará la representación del servidor.
Volviendo a la versión anterior:
Esto resuelve los problemas con la duplicación de la lógica y nos salva de las condiciones de carrera.
Thunk Middleware
Para aplicaciones simples, el enfoque debería ser suficiente. No se preocupe por el middleware si está satisfecho con él.
Sin embargo, en aplicaciones más grandes, puede encontrar ciertos inconvenientes a su alrededor.
Por ejemplo, parece desafortunado que tengamos que pasar
dispatch
. Esto hace que sea más difícil separar el contenedor y los componentes de presentación porque cualquier componente que despacha las acciones de Redux de forma asincrónica de la manera anterior tiene que aceptardispatch
como accesorio para que pueda pasarlo más. Ya no puedes vincular a los creadores de acciónconnect()
porqueshowNotificationWithTimeout()
realmente no es un creador de acción. No devuelve una acción de Redux.Además, puede ser incómodo recordar qué funciones son como los creadores de acciones síncronas
showNotification()
y cuáles son como ayudantes asíncronosshowNotificationWithTimeout()
. Debe usarlos de manera diferente y tener cuidado de no confundirlos entre sí.Esta fue la motivación para encontrar una manera de "legitimar" este patrón de proporcionar
dispatch
una función auxiliar y ayudar a Redux a "ver" a tales creadores de acciones asincrónicas como un caso especial de creadores de acciones normales en lugar de funciones totalmente diferentes.Si todavía está con nosotros y también reconoce como un problema en su aplicación, puede usar el middleware Redux Thunk .
En resumen, Redux Thunk le enseña a Redux a reconocer tipos especiales de acciones que de hecho son funciones:
Cuando este middleware está habilitado, si despacha una función , el middleware Redux Thunk lo dará
dispatch
como argumento. También "tragará" tales acciones, así que no se preocupe si sus reductores reciben argumentos de funciones extrañas. Sus reductores solo recibirán acciones de objetos simples, ya sea emitidas directamente o emitidas por las funciones como acabamos de describir.Esto no parece muy útil, ¿verdad? No en esta situación particular. Sin embargo, nos permite declarar
showNotificationWithTimeout()
como creadores de acciones de Redux:Observe cómo la función es casi idéntica a la que escribimos en la sección anterior. Sin embargo, no acepta
dispatch
como primer argumento. En su lugar, devuelve una función que aceptadispatch
como primer argumento.¿Cómo lo usaríamos en nuestro componente? Definitivamente, podríamos escribir esto:
Estamos llamando al creador de la acción asíncrona para obtener la función interna que quiere justamente
dispatch
, y luego aprobamosdispatch
.¡Sin embargo, esto es aún más incómodo que la versión original! ¿Por qué incluso fuimos por ese camino?
Por lo que te dije antes. Si el middleware Redux Thunk está habilitado, cada vez que intente despachar una función en lugar de un objeto de acción, el middleware llamará a esa función con el
dispatch
método como primer argumento .Entonces podemos hacer esto en su lugar:
Finalmente, despachar una acción asincrónica (realmente, una serie de acciones) no se ve diferente a despachar una sola acción sincrónicamente al componente. Lo cual es bueno porque los componentes no deberían preocuparse si algo sucede sincrónicamente o asincrónicamente. Acabamos de abstraer eso.
Tenga en cuenta que dado que "enseñamos" a Redux a reconocer a tales creadores de acción "especiales" (los llamamos creadores de acción thunk ), ahora podemos usarlos en cualquier lugar donde usaríamos creadores de acción regulares. Por ejemplo, podemos usarlos con
connect()
:Estado de lectura en Thunks
Por lo general, sus reductores contienen la lógica empresarial para determinar el próximo estado. Sin embargo, los reductores solo se activan después de que se envían las acciones. ¿Qué sucede si tiene un efecto secundario (como llamar a una API) en un creador de acciones thunk y desea evitarlo bajo alguna condición?
Sin usar el middleware thunk, solo haría esta verificación dentro del componente:
Sin embargo, el punto de extraer un creador de acción era centralizar esta lógica repetitiva en muchos componentes. Afortunadamente, Redux Thunk le ofrece una forma de leer el estado actual de la tienda Redux. Además de
dispatch
, también pasagetState
como el segundo argumento a la función que devuelve de su creador de acción thunk. Esto permite que el procesador lea el estado actual de la tienda.No abuses de este patrón. Es bueno para rescatar llamadas de API cuando hay datos disponibles en caché, pero no es una muy buena base para construir su lógica de negocios. Si
getState()
solo usa para despachar condicionalmente diferentes acciones, considere colocar la lógica de negocios en los reductores.Próximos pasos
Ahora que tiene una intuición básica sobre cómo funcionan los thunks, consulte el ejemplo asincrónico de Redux que los usa.
Puede encontrar muchos ejemplos en los que los thunks devuelven Promesas. Esto no es obligatorio pero puede ser muy conveniente. A Redux no le importa lo que devuelva de un thunk, pero le da su valor de retorno
dispatch()
. Es por eso que puede devolver una Promesa desde un thunk y esperar a que se complete llamandodispatch(someThunkReturningPromise()).then(...)
.También puede dividir creadores complejos de acción thunk en varios creadores más pequeños de acción thunk. El
dispatch
método proporcionado por thunks puede aceptar thunks por sí mismo, por lo que puede aplicar el patrón de forma recursiva. Nuevamente, esto funciona mejor con Promises porque puede implementar un flujo de control asíncrono además de eso.Para algunas aplicaciones, puede encontrarse en una situación en la que sus requisitos de flujo de control asíncrono son demasiado complejos para expresarse con thunks. Por ejemplo, volver a intentar las solicitudes fallidas, el flujo de reautorización con tokens o una incorporación paso a paso pueden ser demasiado detalladas y propensas a errores cuando se escriben de esta manera. En este caso, es posible que desee ver soluciones de flujo de control asíncrono más avanzadas como Redux Saga o Redux Loop . Evalúelos, compare los ejemplos relevantes para sus necesidades y elija el que más le guste.
Finalmente, no use nada (incluidos los thunks) si no los necesita realmente. Recuerde que, según los requisitos, su solución puede parecer tan simple como
No se preocupe a menos que sepa por qué está haciendo esto.
fuente
if (cond) dispatch({ type: 'A' }) else dispatch({ type: 'B' })
tal vez deberíadispatch({ type: 'C', something: cond })
y elija ignorar la acción en los reductores, dependiendoaction.something
del estado actual.Usando Redux-saga
Como dijo Dan Abramov, si desea un control más avanzado sobre su código asíncrono, puede echar un vistazo a redux-saga .
Esta respuesta es un ejemplo simple, si desea mejores explicaciones sobre por qué redux-saga puede ser útil para su aplicación, consulte esta otra respuesta .
La idea general es que Redux-saga ofrece un intérprete de generadores ES6 que le permite escribir fácilmente código asíncrono que parece código síncrono (es por eso que a menudo encontrará bucles while infinitos en Redux-saga). De alguna manera, Redux-saga está construyendo su propio lenguaje directamente dentro de Javascript. Redux-saga puede parecer un poco difícil de aprender al principio, porque necesita una comprensión básica de los generadores, pero también comprende el lenguaje que ofrece Redux-saga.
Intentaré describir aquí el sistema de notificación que construí sobre redux-saga. Este ejemplo se ejecuta actualmente en producción.
Especificación avanzada del sistema de notificación
Resultado
Captura de pantalla de mi aplicación de producción Stample.co
Código
Aquí llamé a la notificación a
toast
pero este es un detalle de nomenclatura.Y el reductor:
Uso
Simplemente puede enviar
TOAST_DISPLAY_REQUESTED
eventos. Si envía 4 solicitudes, solo se mostrarán 3 notificaciones, y la cuarta aparecerá un poco más tarde una vez que desaparezca la primera notificación.Tenga en cuenta que no recomiendo específicamente enviar
TOAST_DISPLAY_REQUESTED
desde JSX. Prefiere agregar otra saga que escuche sus eventos de aplicaciones ya existentes, y luego enviar elTOAST_DISPLAY_REQUESTED
: su componente que desencadena la notificación, no tiene que estar estrechamente vinculado al sistema de notificación.Conclusión
Mi código no es perfecto, pero se ejecuta en producción con 0 errores durante meses. Redux-saga y los generadores son un poco difíciles inicialmente, pero una vez que los entiendes, este tipo de sistema es bastante fácil de construir.
Incluso es bastante fácil implementar reglas más complejas, como:
Sinceramente, buena suerte implementando este tipo de cosas correctamente con thunks.
Tenga en cuenta que puede hacer exactamente el mismo tipo de cosas con redux-observable, que es muy similar a redux-saga. Es casi lo mismo y es cuestión de gustos entre generadores y RxJS.
fuente
yield call(delay,timeoutValue);
: no es la misma API pero tiene el mismo efectoUn repositorio con proyectos de muestra.
Actualmente hay cuatro proyectos de muestra:
La respuesta aceptada es asombrosa.
Pero falta algo:
Así que creé el repositorio Hello Async para agregar las cosas que faltan:
Redux Saga
La respuesta aceptada ya proporciona fragmentos de código de muestra para Async Code Inline, Async Action Generator y Redux Thunk. En aras de la integridad, proporciono fragmentos de código para Redux Saga:
Las acciones son simples y puras.
Nada es especial con el componente.
Las sagas se basan en generadores ES6
En comparación con Redux Thunk
Pros
Contras
Consulte el proyecto ejecutable si los fragmentos de código anteriores no responden todas sus preguntas.
fuente
Puedes hacer esto con redux-thunk . Hay una guía en el documento redux para acciones asíncronas como setTimeout.
fuente
applyMiddleware(ReduxPromise, thunk)(createStore)
es así como agrega varios middleware (¿coma separado?) Ya que parece que no puedo hacer que funcione.const store = createStore(reducer, applyMiddleware([ReduxPromise, thunk]));
También recomendaría echar un vistazo al patrón SAM .
El patrón SAM aboga por incluir un "predicado de la siguiente acción" donde las acciones (automáticas) como "las notificaciones desaparecen automáticamente después de 5 segundos" se activan una vez que el modelo se ha actualizado (modelo SAM ~ estado reductor + almacén).
El patrón aboga por la secuenciación de acciones y mutaciones del modelo una a la vez, porque el "estado de control" del modelo "controla" qué acciones están habilitadas y / o ejecutadas automáticamente por el predicado de la siguiente acción. Simplemente no puede predecir (en general) en qué estado estará el sistema antes de procesar una acción y, por lo tanto, si su próxima acción esperada será permitida / posible.
Entonces, por ejemplo, el código,
no se permitiría con SAM, porque el hecho de que se pueda enviar una acción hideNotification depende de que el modelo acepte con éxito el valor "showNotication: true". Podría haber otras partes del modelo que eviten que lo acepte y, por lo tanto, no habría razón para activar la acción hideNotification.
Recomiendo encarecidamente que implemente un predicado de próxima acción adecuado después de las actualizaciones de la tienda y se pueda conocer el nuevo estado de control del modelo. Esa es la forma más segura de implementar el comportamiento que está buscando.
Puedes unirte a nosotros en Gitter si quieres. También hay una guía de inicio de SAM disponible aquí .
fuente
V = S( vm( M.present( A(data) ) ), nap(M))
es simplemente hermoso Gracias por compartir tus pensamientos y experiencias. Cavaré más profundo.Después de probar los diversos enfoques populares (creadores de acción, thunks, sagas, epopeyas, efectos, middleware personalizado), todavía sentía que tal vez había margen de mejora, así que documenté mi viaje en este artículo del blog, ¿Dónde pongo mi lógica empresarial? una aplicación React / Redux?
Al igual que las discusiones aquí, traté de contrastar y comparar los diversos enfoques. Finalmente, me llevó a presentar una nueva biblioteca redux-logic que se inspira en epopeyas, sagas, middleware personalizado.
Le permite interceptar acciones para validar, verificar, autorizar, así como proporcionar una forma de realizar IO asíncrona.
Algunas funcionalidades comunes se pueden declarar simplemente como eliminar el rebote, limitar, cancelar y solo usar la respuesta de la última solicitud (takeLatest). redux-logic envuelve su código proporcionando esta funcionalidad para usted.
Eso lo libera para implementar su lógica comercial central como quiera. No tiene que usar observables o generadores a menos que lo desee. Use funciones y devoluciones de llamada, promesas, funciones asíncronas (asíncrono / espera), etc.
El código para hacer una notificación 5s simple sería algo como:
Tengo un ejemplo de notificación más avanzado en mi repositorio que funciona de manera similar a lo que Sebastian Lorber describió, donde podría limitar la visualización a N elementos y rotar a través de cualquiera de los que estaban en cola. ejemplo de notificación de redux-logic
Tengo una variedad de ejemplos en vivo de redux-logic jsfiddle, así como ejemplos completos . Continúo trabajando en documentos y ejemplos.
Me encantaría escuchar tus comentarios.
fuente
Entiendo que esta pregunta es un poco vieja, pero voy a presentar otra solución usando redux-observable aka. Épico.
Citando la documentación oficial:
¿Qué es redux-observable?
Una epopeya es el núcleo primitivo de redux-observable.
En más o menos palabras, puede crear una función que recibe acciones a través de una secuencia y luego devolver una nueva secuencia de acciones (utilizando efectos secundarios comunes como tiempos de espera, retrasos, intervalos y solicitudes).
Permítanme publicar el código y luego explicar un poco más al respecto
store.js
index.js
App.js
El código clave para resolver este problema es tan fácil como se puede ver, lo único que parece diferente de las otras respuestas es la función rootEpic.
Punto 1. Al igual que con las sagas, debe combinar las epopeyas para obtener una función de nivel superior que reciba una secuencia de acciones y devuelva una secuencia de acciones, para que pueda usarla con la fábrica de middleware createEpicMiddleware . En nuestro caso, solo necesitamos uno, así que solo tenemos nuestro rootEpic, por lo que no tenemos que combinar nada, pero es bueno saberlo.
Punto 2. Nuestro rootEpic que se ocupa de la lógica de los efectos secundarios solo toma alrededor de 5 líneas de código, ¡lo cual es increíble! ¡Incluyendo el hecho de que es bastante declarativo!
Punto 3. Línea por línea raíz Explicación épica (en comentarios)
¡Espero que ayude!
fuente
switchMap
?¿Por qué debería ser tan difícil? Es solo lógica de interfaz de usuario. Use una acción dedicada para establecer datos de notificación:
y un componente dedicado para mostrarlo:
En este caso, las preguntas deberían ser "¿cómo se limpia el estado anterior?", "Cómo notificar a un componente que la hora ha cambiado"
Puede implementar alguna acción TIMEOUT que se distribuye en setTimeout desde un componente.
Tal vez esté bien limpiarlo cada vez que se muestre una nueva notificación.
De todos modos, debería haber alguna
setTimeout
en algún lugar, ¿verdad? ¿Por qué no hacerlo en un componente?La motivación es que la funcionalidad de "notificación se desvanece" es realmente una preocupación de la interfaz de usuario. Por lo tanto, simplifica las pruebas para la lógica de su negocio.
No parece tener sentido probar cómo se implementa. Solo tiene sentido verificar cuándo debe expirar la notificación. Por lo tanto, menos código para stub, pruebas más rápidas, código más limpio.
fuente
Si desea manejar el tiempo de espera en acciones selectivas, puede probar el middleware enfoque de . Me enfrenté a un problema similar para manejar selectivamente las acciones basadas en promesas y esta solución fue más flexible.
Digamos que tu creador de acción se ve así:
el tiempo de espera puede contener múltiples valores en la acción anterior
Su implementación de middleware se vería así:
Ahora puede enrutar todas sus acciones a través de esta capa de middleware usando redux.
Puedes encontrar algunos ejemplos similares aquí
fuente
La forma adecuada de hacer esto es usar Redux Thunk, que es un middleware popular para Redux, según la documentación de Redux Thunk:
Básicamente, devuelve una función, y puede retrasar su envío o ponerlo en un estado de condición.
Entonces, algo como esto hará el trabajo por usted:
fuente
Es simple. Usa el paquete trim-redux y escribe así en
componentDidMount
otro lugar y mátalocomponentWillUnmount
.fuente
Redux en sí mismo es una biblioteca bastante detallada, y para tales cosas tendría que usar algo como Redux-thunk , que le dará una
dispatch
función, por lo que podrá enviar el cierre de la notificación después de varios segundos.He creado una biblioteca para abordar problemas como la verbosidad y la componibilidad, y su ejemplo se verá así:
Por lo tanto, redactamos acciones de sincronización para mostrar notificaciones dentro de la acción asíncrona, que puede solicitar información de fondo o verificar más adelante si la notificación se cerró manualmente.
fuente