J'ai une action qui met à jour l'état de notification de ma demande. Habituellement, cette notification sera une erreur ou une information quelconque. Je dois ensuite envoyer une autre action après 5 secondes qui ramènera l'état de notification à celui initial, donc pas de notification. La principale raison derrière cela est de fournir des fonctionnalités où les notifications disparaissent automatiquement après 5 secondes.
Je n'ai eu aucune chance d'utiliser setTimeout
et de retourner une autre action et je ne trouve pas comment cela se fait en ligne. Tout conseil est donc le bienvenu.
redux-saga
réponse basée si vous voulez quelque chose de mieux que les thunks. Réponse tardive, vous devez donc faire défiler longtemps avant de voir apparaître :) ne signifie pas que cela ne vaut pas la peine d'être lu. Voici un raccourci: stackoverflow.com/a/38574266/82609Réponses:
Ne tombez pas dans le piège de penser qu'une bibliothèque devrait prescrire comment tout faire . Si vous voulez faire quelque chose avec un timeout en JavaScript, vous devez utiliser
setTimeout
. Il n'y a aucune raison pour que les actions Redux soient différentes.Redux n'offre des autres moyens de traiter avec des choses asynchrones, mais vous ne devez utiliser ceux lorsque vous réalisez que vous répétez trop de code. Sauf si vous rencontrez ce problème, utilisez ce que la langue propose et optez pour la solution la plus simple.
Écriture de code asynchrone en ligne
C'est de loin le moyen le plus simple. Et il n'y a rien de spécifique à Redux ici.
De même, depuis l'intérieur d'un composant connecté:
La seule différence est que dans un composant connecté, vous n'avez généralement pas accès au magasin lui-même, mais obtenez
dispatch()
des créateurs d'actions spécifiques ou spécifiques. Mais cela ne fait aucune différence pour nous.Si vous n'aimez pas créer des fautes de frappe lors de la répartition des mêmes actions à partir de différents composants, vous souhaiterez peut-être extraire les créateurs d'actions au lieu de répartir les objets d'action en ligne:
Ou, si vous les avez préalablement liés avec
connect()
:Jusqu'à présent, nous n'avons utilisé aucun middleware ou autre concept avancé.
Extraction d'Async Action Creator
L'approche ci-dessus fonctionne très bien dans les cas simples, mais vous constaterez peut-être qu'elle présente quelques problèmes:
HIDE_NOTIFICATION
, masquant par erreur la deuxième notification plus tôt qu'après le délai d'expiration.Pour résoudre ces problèmes, vous devez extraire une fonction qui centralise la logique de temporisation et distribue ces deux actions. Cela pourrait ressembler à ceci:
Désormais, les composants peuvent utiliser
showNotificationWithTimeout
sans dupliquer cette logique ou avoir des conditions de concurrence avec différentes notifications:Pourquoi
showNotificationWithTimeout()
accepte-t-ondispatch
comme premier argument? Parce qu'il doit envoyer des actions au magasin. Normalement, un composant a accèsdispatch
mais comme nous voulons qu'une fonction externe prenne le contrôle de la répartition, nous devons lui donner le contrôle de la répartition.Si vous aviez un magasin singleton exporté à partir d'un module, vous pouvez simplement l'importer
dispatch
directement dessus:Cela semble plus simple mais nous ne recommandons pas cette approche . La principale raison pour laquelle nous ne l'aimons pas est qu'il force le magasin à être un singleton . Cela rend très difficile l'implémentation du rendu du serveur . Sur le serveur, vous souhaiterez que chaque demande ait son propre magasin, de sorte que différents utilisateurs obtiennent des données préchargées différentes.
Un magasin singleton rend également les tests plus difficiles. Vous ne pouvez plus vous moquer d'un magasin lors du test des créateurs d'actions, car ils font référence à un magasin réel spécifique exporté à partir d'un module spécifique. Vous ne pouvez même pas réinitialiser son état de l'extérieur.
Donc, bien que vous puissiez techniquement exporter un magasin singleton à partir d'un module, nous le déconseillons. Ne faites cela que si vous êtes sûr que votre application n'ajoutera jamais de rendu de serveur.
Revenir à la version précédente:
Cela résout les problèmes de duplication de la logique et nous évite les conditions de course.
Thunk Middleware
Pour les applications simples, l'approche devrait suffire. Ne vous inquiétez pas du middleware si vous en êtes satisfait.
Cependant, dans les applications plus grandes, vous pouvez rencontrer certains inconvénients.
Par exemple, il semble regrettable que nous devions faire le
dispatch
tour. Il est donc plus difficile de séparer les conteneurs et les composants de présentation, car tout composant qui distribue des actions Redux de manière asynchrone de la manière ci-dessus doit accepterdispatch
comme accessoire pour pouvoir le transmettre plus loin. Vous ne pouvez plus simplement lier des créateurs d'actionconnect()
car ceshowNotificationWithTimeout()
n'est pas vraiment un créateur d'action. Il ne renvoie pas d'action Redux.De plus, il peut être difficile de se souvenir des fonctions comme les créateurs d'actions synchrones
showNotification()
et celles des assistants asynchronesshowNotificationWithTimeout()
. Vous devez les utiliser différemment et veillez à ne pas les confondre.C'était la motivation pour trouver un moyen de «légitimer» ce modèle de fourniture
dispatch
d'une fonction d'aide, et aider Redux à «voir» ces créateurs d'actions asynchrones comme un cas spécial de créateurs d'actions normales plutôt que des fonctions totalement différentes.Si vous êtes toujours avec nous et que vous reconnaissez également un problème dans votre application, vous pouvez utiliser le middleware Redux Thunk .
Dans un sens, Redux Thunk enseigne à Redux à reconnaître des types spéciaux d'actions qui sont en fait des fonctions:
Lorsque ce middleware est activé, si vous distribuez une fonction , le middleware Redux Thunk la donnera
dispatch
en argument. Il "avalera" également de telles actions, alors ne vous inquiétez pas si vos réducteurs reçoivent des arguments de fonction étranges. Vos réducteurs ne recevront que des actions d'objets simples, soit émises directement, soit émises par les fonctions comme nous venons de le décrire.Cela ne semble pas très utile, n'est-ce pas? Pas dans cette situation particulière. Cependant, cela nous permet de déclarer en
showNotificationWithTimeout()
tant que créateur d'actions Redux:Notez que la fonction est presque identique à celle que nous avons écrite dans la section précédente. Cependant, il n'accepte pas
dispatch
comme premier argument. Au lieu de cela, il renvoie une fonction qui acceptedispatch
comme premier argument.Comment l'utiliserions-nous dans notre composant? Certainement, nous pourrions écrire ceci:
Nous appelons le créateur d'action asynchrone pour obtenir la fonction intérieure qui veut juste
dispatch
, puis nous passonsdispatch
.Cependant, c'est encore plus gênant que la version originale! Pourquoi avons-nous même choisi cette voie?
À cause de ce que je vous ai dit auparavant. Si le middleware Redux Thunk est activé, chaque fois que vous essayez de distribuer une fonction au lieu d'un objet action, le middleware appellera cette fonction avec la
dispatch
méthode elle-même comme premier argument .Nous pouvons donc le faire à la place:
Enfin, la répartition d'une action asynchrone (en réalité, une série d'actions) ne ressemble pas à la répartition d'une seule action de manière synchrone sur le composant. Ce qui est bien car les composants ne devraient pas se soucier de savoir si quelque chose se produit de manière synchrone ou asynchrone. Nous avons simplement résumé cela.
Notez que depuis que nous avons « appris » Redux reconnaître ces créateurs d'action « spéciaux » (nous les appelons thunk créateurs d'action), nous pouvons maintenant les utiliser dans un endroit où nous utiliserions les créateurs d'action réguliers. Par exemple, nous pouvons les utiliser avec
connect()
:État de lecture dans Thunks
Habituellement, vos réducteurs contiennent la logique métier pour déterminer l'état suivant. Cependant, les réducteurs n'interviennent qu'après l'envoi des actions. Que se passe-t-il si vous avez un effet secondaire (comme appeler une API) dans un créateur d'action thunk, et que vous souhaitez l'empêcher dans certaines conditions?
Sans utiliser le middleware thunk, vous devez simplement effectuer cette vérification à l'intérieur du composant:
Cependant, le but d'extraire un créateur d'action était de centraliser cette logique répétitive sur de nombreux composants. Heureusement, Redux Thunk vous offre un moyen de lire l'état actuel de la boutique Redux. De plus
dispatch
, il passe égalementgetState
comme deuxième argument à la fonction que vous renvoyez de votre créateur d'action thunk. Cela permet au thunk de lire l'état actuel du magasin.N'abusez pas de ce modèle. C'est bon pour échapper aux appels d'API lorsqu'il y a des données en cache disponibles, mais ce n'est pas une très bonne base pour construire votre logique métier. Si vous utilisez
getState()
uniquement pour répartir conditionnellement différentes actions, envisagez plutôt de placer la logique métier dans les réducteurs.Prochaines étapes
Maintenant que vous avez une intuition de base sur le fonctionnement des thunks, consultez l' exemple asynchrone Redux qui les utilise.
Vous pouvez trouver de nombreux exemples dans lesquels les thunks retournent des promesses. Ce n'est pas obligatoire mais peut être très pratique. Redux ne se soucie pas de ce que vous revenez d'un thunk, mais il vous donne sa valeur de retour
dispatch()
. C'est pourquoi vous pouvez retourner une promesse d'un thunk et attendre qu'elle se termine en appelantdispatch(someThunkReturningPromise()).then(...)
.Vous pouvez également diviser les créateurs d'actions de thunk complexes en plusieurs créateurs d'action de thunk plus petits. La
dispatch
méthode fournie par thunks peut accepter les thunks elle-même, vous pouvez donc appliquer le modèle de manière récursive. Encore une fois, cela fonctionne mieux avec Promises, car vous pouvez implémenter un flux de contrôle asynchrone en plus de cela.Pour certaines applications, vous pouvez vous retrouver dans une situation où vos exigences de flux de contrôle asynchrone sont trop complexes pour être exprimées avec des thunks. Par exemple, une nouvelle tentative de demandes ayant échoué, un flux de réautorisation avec des jetons ou une intégration étape par étape peuvent être trop verbeux et sujets aux erreurs lorsqu'ils sont écrits de cette façon. Dans ce cas, vous souhaiterez peut-être examiner des solutions de flux de contrôle asynchrones plus avancées telles que Redux Saga ou Redux Loop . Évaluez-les, comparez les exemples pertinents à vos besoins et choisissez celui que vous aimez le plus.
Enfin, n'utilisez rien (y compris les thunks) si vous n'en avez pas vraiment besoin. N'oubliez pas que, selon les exigences, votre solution peut sembler aussi simple que
Ne le transpirez que si vous savez pourquoi vous faites cela.
la source
if (cond) dispatch({ type: 'A' }) else dispatch({ type: 'B' })
vous devriez peut-être justedispatch({ type: 'C', something: cond })
choisir d'ignorer l'action dans les réducteurs en fonction deaction.something
l'état actuel.Utiliser Redux-saga
Comme l'a dit Dan Abramov, si vous voulez un contrôle plus avancé sur votre code asynchrone, vous pouvez jeter un œil à redux-saga .
Cette réponse est un exemple simple, si vous voulez de meilleures explications sur la raison pour laquelle redux-saga peut être utile pour votre application, vérifiez cette autre réponse .
L'idée générale est que Redux-saga propose un interpréteur de générateurs ES6 qui vous permet d'écrire facilement du code asynchrone qui ressemble à du code synchrone (c'est pourquoi vous trouverez souvent des boucles while infinies dans Redux-saga). D'une certaine manière, Redux-saga construit son propre langage directement à l'intérieur de Javascript. Redux-saga peut sembler un peu difficile à apprendre au début, car vous avez besoin d'une compréhension de base des générateurs, mais aussi de comprendre le langage proposé par Redux-saga.
Je vais essayer ici de décrire ici le système de notification que j'ai construit au-dessus de redux-saga. Cet exemple fonctionne actuellement en production.
Spécification du système de notification avancé
Résultat
Capture d'écran de mon application de production Stample.co
Code
Ici, j'ai nommé la notification a
toast
mais c'est un détail de dénomination.Et le réducteur:
Usage
Vous pouvez simplement envoyer des
TOAST_DISPLAY_REQUESTED
événements. Si vous envoyez 4 demandes, seules 3 notifications seront affichées, et la 4ème apparaîtra un peu plus tard une fois la 1ère notification disparue.Notez que je ne recommande pas spécifiquement l'envoi
TOAST_DISPLAY_REQUESTED
depuis JSX. Vous préférez ajouter une autre saga qui écoute vos événements d'application déjà existants, puis distribuer leTOAST_DISPLAY_REQUESTED
: votre composant qui déclenche la notification, n'a pas besoin d'être étroitement couplé au système de notification.Conclusion
Mon code n'est pas parfait mais tourne en production avec 0 bogue pendant des mois. Redux-saga et les générateurs sont un peu difficiles au début, mais une fois que vous les comprenez, ce type de système est assez facile à construire.
Il est même assez facile d'implémenter des règles plus complexes, comme:
Honnêtement, bonne chance pour implémenter ce genre de choses correctement avec des thunks.
Notez que vous pouvez faire exactement le même genre de chose avec redux-observable qui est très similaire à redux-saga. C'est presque la même chose et c'est une question de goût entre les générateurs et RxJS.
la source
yield call(delay,timeoutValue);
: ce n'est pas la même API mais elle a le même effetUn référentiel avec des exemples de projets
Actuellement, il existe quatre exemples de projets:
La réponse acceptée est impressionnante.
Mais il manque quelque chose:
J'ai donc créé le référentiel Hello Async pour ajouter les éléments manquants:
Redux Saga
La réponse acceptée fournit déjà des exemples d'extraits de code pour Async Code Inline, Async Action Generator et Redux Thunk. Par souci d'exhaustivité, je fournis des extraits de code pour Redux Saga:
Les actions sont simples et pures.
Rien de spécial avec le composant.
Les Sagas sont basées sur des générateurs ES6
Par rapport à Redux Thunk
Avantages
Les inconvénients
Veuillez vous référer au projet exécutable si les extraits de code ci-dessus ne répondent pas à toutes vos questions.
la source
Vous pouvez le faire avec redux-thunk . Il y a un guide dans le document redux pour les actions asynchrones comme setTimeout.
la source
applyMiddleware(ReduxPromise, thunk)(createStore)
comment vous ajoutez plusieurs middleware (séparés par un coma?) Car je n'arrive pas à faire fonctionner le thunk.const store = createStore(reducer, applyMiddleware([ReduxPromise, thunk]));
Je recommanderais également de jeter un œil au modèle SAM .
Le modèle SAM préconise d'inclure un "prédicat d'action suivante" où des actions (automatiques) telles que "les notifications disparaissent automatiquement après 5 secondes" sont déclenchées une fois que le modèle a été mis à jour (modèle SAM ~ état réducteur + magasin).
Le modèle préconise de séquencer les actions et les mutations du modèle une à la fois, car "l'état de contrôle" du modèle "contrôle" les actions qui sont activées et / ou exécutées automatiquement par le prédicat de l'action suivante. Vous ne pouvez tout simplement pas prédire (en général) quel état sera le système avant de traiter une action et donc si votre prochaine action attendue sera autorisée / possible.
Ainsi, par exemple, le code,
ne serait pas autorisé avec SAM, car le fait qu'une action hideNotification puisse être envoyée dépend du fait que le modèle accepte avec succès la valeur "showNotication: true". Il pourrait y avoir d'autres parties du modèle qui l'empêchent de l'accepter et, par conséquent, il n'y aurait aucune raison de déclencher l'action hideNotification.
Je recommanderais fortement d'implémenter un prédicat d'action suivante approprié après les mises à jour du magasin et le nouvel état de contrôle du modèle peut être connu. C'est le moyen le plus sûr de mettre en œuvre le comportement que vous recherchez.
Vous pouvez nous rejoindre sur Gitter si vous le souhaitez. Un guide de démarrage SAM est également disponible ici .
la source
V = S( vm( M.present( A(data) ) ), nap(M))
est tout simplement magnifique. Merci d'avoir partagé vos pensées et votre expérience. Je vais creuser plus profondément.Après avoir essayé les différentes approches populaires (créateurs d'actions, thunks, sagas, épopées, effets, middleware personnalisé), je sentais encore qu'il y avait peut-être des améliorations à apporter, j'ai donc documenté mon parcours dans cet article de blog, Où mettre ma logique métier dans une application React / Redux?
Tout comme les discussions ici, j'ai essayé de contraster et de comparer les différentes approches. Finalement, cela m'a amené à introduire une nouvelle bibliothèque redux-logic qui s'inspire des épopées, des sagas, des middleware personnalisés.
Il vous permet d'intercepter des actions pour valider, vérifier, autoriser, ainsi que de fournir un moyen d'effectuer des E / S asynchrones.
Certaines fonctionnalités courantes peuvent simplement être déclarées comme le rebond, la limitation, l'annulation et uniquement en utilisant la réponse de la dernière demande (takeLatest). redux-logic encapsule votre code en vous fournissant cette fonctionnalité.
Cela vous libère pour mettre en œuvre votre logique métier de base comme vous le souhaitez. Vous n'avez pas besoin d'utiliser des observables ou des générateurs, sauf si vous le souhaitez. Utilisez des fonctions et des rappels, des promesses, des fonctions asynchrones (async / attente), etc.
Le code pour faire une simple notification 5s serait quelque chose comme:
J'ai un exemple de notification plus avancé dans mon référentiel qui fonctionne de manière similaire à ce que Sebastian Lorber a décrit où vous pouviez limiter l'affichage à N éléments et faire pivoter tout ce qui était en file d'attente. exemple de notification redux-logic
J'ai une variété d' exemples live jsfiddle redux-logic ainsi que des exemples complets . Je continue de travailler sur des documents et des exemples.
J'adorerais entendre vos commentaires.
la source
Je comprends que cette question est un peu ancienne mais je vais introduire une autre solution utilisant aka redux-observable . Épique.
Citant la documentation officielle:
Qu'est-ce qui est observable de nouveau?
Une épopée est la principale primitive de redux-observable.
En plus ou moins de mots, vous pouvez créer une fonction qui reçoit des actions via un flux, puis renvoyer un nouveau flux d'actions (en utilisant des effets secondaires courants tels que les délais d'expiration, les retards, les intervalles et les demandes).
Permettez-moi de poster le code, puis d'expliquer un peu plus à ce sujet
store.js
index.js
App.js
Le code clé pour résoudre ce problème est aussi simple que vous pouvez le voir, la seule chose qui semble différente des autres réponses est la fonction rootEpic.
Point 1. Comme pour les sagas, vous devez combiner les épopées afin d'obtenir une fonction de niveau supérieur qui reçoit un flux d'actions et renvoie un flux d'actions, afin que vous puissiez l'utiliser avec la fabrique de middleware createEpicMiddleware . Dans notre cas, nous n'en avons besoin que d'un, nous n'avons donc que notre rootEpic , nous n'avons donc pas à combiner quoi que ce soit, mais c'est un fait bien connu.
Point 2. Notre rootEpic qui s'occupe de la logique des effets secondaires ne prend que 5 lignes de code, ce qui est génial! Y compris le fait qui est à peu près déclaratif!
Point 3. Explication racine par ligne rootEpic (dans les commentaires)
J'espère que ça aide!
la source
switchMap
?Pourquoi cela devrait-il être si difficile? C'est juste une logique d'interface utilisateur. Utilisez une action dédiée pour définir les données de notification:
et un composant dédié pour l'afficher:
Dans ce cas, les questions doivent être "comment nettoyer l'ancien état?", "Comment avertir un composant que l'heure a changé"
Vous pouvez implémenter une action TIMEOUT qui est distribuée sur setTimeout à partir d'un composant.
Peut-être que c'est bien de le nettoyer chaque fois qu'une nouvelle notification est affichée.
Quoi qu'il en soit, il devrait y en avoir quelque
setTimeout
part, non? Pourquoi ne pas le faire dans un composantLa motivation est que la fonctionnalité "notification fade out" est vraiment une préoccupation d'interface utilisateur. Cela simplifie donc les tests de votre logique métier.
Il ne semble pas logique de tester comment il est mis en œuvre. Il est logique de vérifier quand la notification doit expirer. Ainsi moins de code à stub, des tests plus rapides, un code plus propre.
la source
Si vous souhaitez gérer la temporisation sur des actions sélectives, vous pouvez essayer le middleware approche . J'ai rencontré un problème similaire pour gérer les actions basées sur les promesses de manière sélective et cette solution était plus flexible.
Disons que votre créateur d'action ressemble à ceci:
le délai d'expiration peut contenir plusieurs valeurs dans l'action ci-dessus
L'implémentation de votre middleware ressemblerait à ceci:
Vous pouvez désormais acheminer toutes vos actions via cette couche middleware à l'aide de redux.
Vous pouvez trouver des exemples similaires ici
la source
La façon appropriée de le faire est d'utiliser Redux Thunk qui est un middleware populaire pour Redux, selon la documentation de Redux Thunk:
Donc, fondamentalement, il renvoie une fonction, et vous pouvez retarder votre envoi ou le mettre dans un état de condition.
Donc, quelque chose comme ça va faire le travail pour vous:
la source
C'est simple. Utilisez le package trim-redux et écrivez comme ceci dans
componentDidMount
ou ailleurs et tuez-lecomponentWillUnmount
.la source
Redux lui-même est une bibliothèque assez bavarde, et pour de telles choses, vous devrez utiliser quelque chose comme Redux-thunk , qui donnera une
dispatch
fonction, vous pourrez donc envoyer la fermeture de la notification après plusieurs secondes.J'ai créé une bibliothèque pour résoudre des problèmes tels que la verbosité et la composabilité, et votre exemple ressemblera à ceci:
Nous composons donc des actions de synchronisation pour afficher les notifications dans l'action asynchrone, qui peut demander des informations en arrière-plan, ou vérifier plus tard si la notification a été fermée manuellement.
la source