web-dev-qa-db-fra.com

Les magasins de flux ou les actions (ou les deux) doivent-ils toucher à des services externes?

Les magasins doivent-ils conserver leur propre état et être en mesure d'appeler des services de réseau et de stockage de données, auquel cas les actions ne sont que des passeurs de messages stupides,

-OU-

... les magasins devraient-ils être des destinataires idiots de données immuables issues des actions (et les actions étant celles qui extraient/envoient des données entre des sources externes? Store dans ce cas servirait de modèle de vue et permettrait données avant de définir leur propre base d’état sur les données immuables alimentées par l’action.

Il me semble que ce devrait être l'un ou l'autre (plutôt qu'un mélange des deux). Si oui, pourquoi l’un est-il préféré/recommandé à l’autre?

122
plaxdan

J'ai vu le modèle de flux mis en œuvre dans les deux sens, et après l'avoir fait moi-même (initialement avec l'approche précédente), je pense que les magasins devraient être des destinataires stupides des données des actions, et que le traitement asynchrone des écritures devrait demeurer dans la mémoire. créateurs d'action. ( Les lectures asynchrones peuvent être gérées différemment .) D'après mon expérience, cela présente quelques avantages, par ordre d'importance:

  1. Vos magasins deviennent complètement synchrones. Cela rend la logique de votre magasin beaucoup plus facile à suivre et très facile à tester: il suffit d'instancier un magasin avec un état donné, puis de l'envoyer. une action et vérifiez si l'état a changé comme prévu. En outre, l’un des concepts fondamentaux de Flux est de prévenir les envois en cascade et d’empêcher les envois multiples à la fois; C'est très difficile à faire lorsque vos magasins effectuent un traitement asynchrone.

  2. Toutes les actions sont distribuées par les créateurs d’action. Si vous gérez des opérations asynchrones dans vos magasins et que vous souhaitez que les gestionnaires d’action de vos magasins soient synchrones (et vous devriez le faire). afin d’obtenir les garanties de flux unique), vos magasins devront déclencher des actions supplémentaires SUCCESS et FAIL en réponse au traitement asynchrone. Le fait de placer ces messages dans les créateurs d’action contribue à séparer les travaux des créateurs d’action et des magasins; De plus, vous n'avez pas besoin de fouiller dans la logique de votre magasin pour déterminer d'où les actions sont envoyées. Une action asynchrone typique dans ce cas pourrait ressembler à ceci (changer la syntaxe des appels dispatch en fonction de la nature du flux que vous utilisez):

    someActionCreator: function(userId) {
      // Dispatch an action now so that stores that want
      // to optimistically update their state can do so.
      dispatch("SOME_ACTION", {userId: userId});
    
      // This example uses promises, but you can use Node-style
      // callbacks or whatever you want for error handling.
      SomeDataAccessLayer.doSomething(userId)
      .then(function(newData) {
        // Stores that optimistically updated may not do anything
        // with a "SUCCESS" action, but you might e.g. stop showing
        // a loading indicator, etc.
        dispatch("SOME_ACTION_SUCCESS", {userId: userId, newData: newData});
      }, function(error) {
        // Stores can roll back by watching for the error case.
        dispatch("SOME_ACTION_FAIL", {userId: userId, error: error});
      });
    }
    

    La logique qui pourrait autrement être dupliquée à travers différentes actions devrait être extraite dans un module séparé; dans cet exemple, ce module serait SomeDataAccessLayer, qui gère la demande Ajax réelle.

  3. Vous avez besoin de moins de créateurs d'action. C'est moins grave, mais c'est bien d'avoir. Comme indiqué au point 2, si vos magasins disposent d'une fonction de répartition des actions synchrones (et ils le devraient), vous devez déclencher des actions supplémentaires pour gérer les résultats des opérations asynchrones. Faire les répartitions dans les créateurs d'action signifie qu'un créateur d'action unique peut envoyer les trois types d'action en gérant le résultat de l'accès de données asynchrone lui-même.

151
Michelle Tilley

J'ai tweeté cette question aux développeurs de Facebook et la réponse que j'ai obtenue de Bill Fisher était:

Lorsque je répondais à l'interaction d'un utilisateur avec l'interface utilisateur, je faisais l'appel asynchrone dans les méthodes du créateur d'actions.

Mais lorsque vous avez un téléscripteur ou un autre pilote non humain, un appel du magasin fonctionne mieux.

L'important est de créer une action dans le rappel d'erreur/de succès afin que les données proviennent toujours d'actions

51
Markus-ipse

Les magasins doivent tout faire, y compris récupérer des données et signaler aux composants que les données du magasin ont été mises à jour. Pourquoi? Parce que les actions peuvent alors être légères, jetables et remplaçables sans influencer les comportements importants. Tous les comportements et fonctionnalités importants se produisent dans le magasin. Cela évite également la duplication de comportements qui seraient autrement copiés dans deux actions très similaires mais différentes. Les magasins sont votre célibataire source de (manipulant) la vérité.

Dans chaque implémentation de Flux, les actions sont essentiellement des chaînes d'événement transformées en objets, comme si vous aviez un événement nommé "anchor: clicked", mais dans Flux, il serait défini comme AnchorActions.Clicked. Ils sont même si "stupides" que la plupart des implémentations ont des objets Dispatcher distincts pour distribuer les événements aux magasins en écoute.

Personnellement, j'aime bien l'implémentation de Flux dans Reflux, où il n'y a pas d'objets Dispatcher distincts et les objets Action effectuent eux-mêmes la distribution.


edit: Le flux de Facebook va en fait dans les "créateurs d'action", ils utilisent donc des actions intelligentes. Ils préparent également la charge utile en utilisant les magasins:

https://github.com/facebook/flux/blob/19a24975462234ddc583ad740354e115c20b881d/examples/flux-chat/js/actions/ChatMessageActionCreators.js#L27 (ligne 27 et 28)

Le rappel à la fin déclencherait alors une nouvelle action cette fois avec les données extraites en tant que données utiles:

https://github.com/facebook/flux/blob/19a24975462234ddc583ad740354e115c20b881d/examples/flux-chat/js/utils/ChatWebAPIUtils.js#L51

Donc, je suppose que c'est la meilleure solution.

8
Rygu

Je vais fournir un argument en faveur des actions "stupides".

En plaçant la responsabilité de la collecte des données de vue dans vos actions, vous couplez vos actions aux exigences de données de vos vues.

En revanche, les actions génériques, qui décrivent de manière déclarative le intention de l'utilisateur, ou une transition d'état dans votre application, permettent à tout magasin qui répond à cette action de transformer l'intention en un état spécialement adapté aux vues. abonné à elle.

Cela se prête à des magasins plus nombreux, mais plus petits et plus spécialisés. Je plaide pour ce style parce que

  • cela vous donne plus de flexibilité dans la façon dont les vues consomment les données de la boutique.
  • Les magasins "intelligents", spécialisés pour les vues qui les consomment, seront plus petits et moins couplés pour les applications complexes que les actions "intelligentes", dont dépendent potentiellement de nombreuses vues.

Le but d'un magasin est de fournir des données aux vues. Le nom "Action" me suggère que son but est de décrire un changement dans mon application.

Supposons que vous deviez ajouter un widget à une vue Dashboard existante, qui présente de nouvelles données agrégées fantaisistes que votre équipe back-end vient de déployer.

Avec les actions "intelligentes", vous devrez peut-être modifier votre action "actualiser le tableau de bord" pour utiliser la nouvelle API. Cependant, "Rafraîchir le tableau de bord" dans un sens abstrait n'a pas changé. Les exigences en matière de données de vos vues sont ce qui a changé.

Avec des actions "stupides", vous pouvez ajouter un nouveau magasin au nouveau widget, et le configurer de sorte que, lorsqu'il reçoit le type d'action "refresh-dashboard", il envoie une demande pour les nouvelles données et les expose à le nouveau widget une fois qu'il est prêt. Il est logique pour moi que lorsque la couche de visualisation a besoin de plus de données ou de données différentes, ce que je change est la source de ces données: Magasins.

3
Carlos Lalimarmo

gaeron's flux-react-router-demo a une variante utilitaire de Nice de l'approche "correcte".

Un ActionCreator génère une promesse à partir d'un service d'API externe, puis passe la promesse et les trois constantes d'action à une fonction dispatchAsync dans un répartiteur proxy/étendu. dispatchAsync enverra toujours la première action, par exemple. 'GET_EXTERNAL_DATA' et une fois que la promesse est retournée, elle envoie soit 'GET_EXTERNAL_DATA_SUCCESS' ou 'GET_EXTERNAL_DATA_ERROR'.

2
William Myers

Si vous souhaitez un jour disposer d'un environnement de développement comparable à celui de la célèbre vidéo de Bret Victor Inventer selon le principe , vous devriez plutôt utiliser des magasins stupides qui ne sont que des projections d'actions/événements dans une structure de données. , sans aucun effet secondaire. Il serait également utile que vos magasins soient réellement membres de la même structure de données globale immuable, comme dans Redux .

Plus d'explications ici: https://stackoverflow.com/a/31388262/82609

1
Sebastien Lorber