web-dev-qa-db-fra.com

Où la demande ajax doit-elle être faite dans l'application Flux?

Je crée une application react.js avec une architecture de flux et j'essaie de déterminer où et quand une demande de données du serveur doit être faite. Y at-il un exemple pour cela. (Pas l'application TODO!)

190
Eniz Gülek

Je suis un grand partisan de l'insertion d'opérations d'écriture asynchrone dans les créateurs d'action et d'opérations de lecture asynchrone dans le magasin. L'objectif est de conserver le code de modification de l'état du magasin dans des gestionnaires d'actions entièrement synchrones. cela les rend simples à raisonner et simples à tester les unités. Afin d'éviter plusieurs demandes simultanées sur le même point de terminaison (par exemple, la lecture double), je vais déplacer le traitement de la demande dans un module séparé qui utilise des promesses pour empêcher les demandes multiples. par exemple:

class MyResourceDAO {
  get(id) {
    if (!this.promises[id]) {
      this.promises[id] = new Promise((resolve, reject) => {
        // ajax handling here...
      });
    } 
    return this.promises[id];
  }
}

Bien que les lectures dans le magasin impliquent des fonctions asynchrones, il y a une mise en garde importante: les magasins ne se mettent pas à jour dans les gestionnaires asynchrones, mais déclenchent une action et uniquement une action lorsque la réponse arrive. Les gestionnaires de cette action finissent par modifier l'état actuel.

Par exemple, un composant peut faire:

getInitialState() {
  return { data: myStore.getSomeData(this.props.id) };
}

Le magasin aurait une méthode implémentée, peut-être, quelque chose comme ceci:

class Store {
  getSomeData(id) {
    if (!this.cache[id]) {
      MyResurceDAO.get(id).then(this.updateFromServer);
      this.cache[id] = LOADING_TOKEN;
      // LOADING_TOKEN is a unique value of some kind
      // that the component can use to know that the
      // value is not yet available.
    }

    return this.cache[id];
  }

  updateFromServer(response) {
    fluxDispatcher.dispatch({
      type: "DATA_FROM_SERVER",
      payload: {id: response.id, data: response}
    });
  }

  // this handles the "DATA_FROM_SERVER" action
  handleDataFromServer(action) {
    this.cache[action.payload.id] = action.payload.data;
    this.emit("change"); // or whatever you do to re-render your app
  }
}
125
Michelle Tilley

Fluxxor a un exemple de communication asynchrone avec une API.

Ce blog a fait l'objet de discussions et a été présenté sur le blog de React.


Je trouve cette question très importante et difficile, à laquelle il n’a pas encore été clairement répondu, car la synchronisation des logiciels entre le système et le système est toujours difficile.

Les demandes d'API doivent-elles être effectuées dans des composants JSX? Magasins? Autre endroit?

Exécuter des requêtes dans les magasins signifie que si 2 magasins ont besoin des mêmes données pour une action donnée, ils émettront 2 requêtes similaires (sauf si vous introduisez des dépendances entre magasins, ce que je n'aime pas du tout )

Dans mon cas, j’ai trouvé très pratique de mettre les promesses Q comme une charge utile d’actions car:

  • Mes actions n'ont pas besoin d'être sérialisables (je ne tiens pas de journal des événements, je n'ai pas besoin de la fonction de relecture des événements de la recherche d'événement)
  • Cela élimine le besoin d'avoir différentes actions/événements (demande déclenchée/demande complétée/demande échouée) et doit les faire correspondre à l'aide d'identificateurs de corrélation lorsque des demandes simultanées peuvent être déclenchées.
  • Cela permet à plusieurs magasins d'écouter l'achèvement de la même demande, sans introduire de dépendance entre les magasins (cependant, il peut être préférable d'introduire une couche de mise en cache?)

Ajax est EVIL

Je pense que l’Ajax sera de moins en moins utilisé dans un proche avenir car il est très difficile de raisonner à propos de… .. La bonne manière? Considérer les périphériques comme faisant partie du système distribué Je ne sais pas où je suis tombé sur cette idée pour la première fois (peut-être dans cette vidéo inspirante de Chris Granger ).

Penses-y. Maintenant, pour l'évolutivité, nous utilisons des systèmes distribués avec une cohérence éventuelle en tant que moteurs de stockage (car nous ne pouvons pas battre le théorème CAP et nous voulons souvent être disponibles). Ces systèmes ne se synchronisent pas en s'interrogeant les uns sur les autres (sauf peut-être pour des opérations consensuelles?), Mais utilisent plutôt des structures telles que CRDT et des journaux d'événements pour rendre tous les membres du système distribué cohérents (les membres convergeront vers les mêmes données, avec suffisamment de temps) .

Maintenant, réfléchissez à ce qu'est un appareil mobile ou un navigateur. Il s’agit simplement d’un membre du système distribué susceptible de souffrir de latence réseau et de partitionnement réseau. (c.-à-d. que vous utilisez votre smartphone dans le métro)

Si nous pouvons construire des bases de données réseau et des bases de données tolérantes à la vitesse du réseau (nous pouvons toujours effectuer des opérations d'écriture sur un nœud isolé), nous pouvons probablement créer des logiciels frontaux (mobiles ou de bureau) inspirés de ces concepts, qui fonctionnent bien avec le mode hors connexion pris en charge. de la boîte sans indisponibilité des fonctionnalités de l'application.

Je pense que nous devrions vraiment nous inspirer de la manière dont les bases de données fonctionnent pour l'architecture de nos applications frontales. Une chose à noter est que ces applications n'effectuent pas les demandes POST et PUT et GET ajax d'envoyer des données les unes aux autres, mais utilisent plutôt les journaux des événements et CRDT pour assurer une cohérence éventuelle. 

Alors pourquoi ne pas le faire sur le front-end? Notez que le back-end va déjà dans cette direction, avec des outils comme Kafka massivement adoptés par les gros joueurs. Cela est en quelque sorte lié à Event Sourcing/CQRS/DDD également.

Vérifiez ces articles impressionnants d'auteurs de Kafka pour vous convaincre:

Nous pourrions peut-être commencer par envoyer des commandes au serveur et recevoir un flux d’événements du serveur (via Websockets par exemple) au lieu de lancer des requêtes Ajax.

Je n'ai jamais été très à l'aise avec les demandes Ajax. Les développeurs de React ont tendance à être des programmeurs fonctionnels. Je pense qu'il est difficile de raisonner sur des données locales qui sont supposées être votre "source de vérité" de votre application frontale, alors que la vraie source de vérité se trouve en fait sur la base de données du serveur, et que votre source de vérité "locale" est peut-être déjà dépassée. lorsque vous le recevez, et ne convergera jamais vers la source réelle de valeur de vérité à moins que vous n'appuyiez sur un bouton de rafraîchissement boiteux ... Est-ce l'ingénierie?

Cependant, il est encore un peu difficile de concevoir une telle chose pour des raisons évidentes:

  • Votre client mobile/navigateur dispose de ressources limitées et ne peut pas nécessairement stocker toutes les données localement (ce qui nécessite parfois une interrogation avec un contenu lourd de demande ajax)
  • Votre client ne doit pas voir toutes les données du système distribué, il faut donc filtrer les événements qu'il reçoit pour des raisons de sécurité.
37
Sebastien Lorber

Vous pouvez appeler des données dans les créateurs d’action ou dans les magasins. L'important est de ne pas gérer la réponse directement, mais de créer une action dans le rappel d'erreur/de succès. Le traitement de la réponse directement dans le magasin conduit à une conception plus fragile.

20
fisherwebdev

Je me sers de l'exemple de Binary Muse tiré de exemple de Fluxxor ajax . Voici mon exemple très simple utilisant la même approche.

J'ai un simple magasin de produits certains actions de produit et le vue du contrôleur composant qui a des sous-composants qui répondent tous aux changements apportés à la magasin de produits. Par exemple produit-sliderliste de produits et recherche de produit Composants.

Faux produit client

Voici le faux client que vous pouvez utiliser pour appeler un produit final renvoyant des produits.

var ProductClient = {

  load: function(success, failure) {
    setTimeout(function() {
      var ITEMS = require('../data/product-data.js');
      success(ITEMS);
    }, 1000);
  }    
};

module.exports = ProductClient;

Magasin de produits

Voici le magasin de produits, évidemment, il s’agit d’un magasin très minimal.

var Fluxxor = require("fluxxor");

var store = Fluxxor.createStore({

  initialize: function(options) {

    this.productItems = [];

    this.bindActions(
      constants.LOAD_PRODUCTS_SUCCESS, this.onLoadSuccess,
      constants.LOAD_PRODUCTS_FAIL, this.onLoadFail
    );
  },

  onLoadSuccess: function(data) {    
    for(var i = 0; i < data.products.length; i++){
      this.productItems.Push(data.products[i]);
    }    
    this.emit("change");
  },

  onLoadFail: function(error) {
    console.log(error);    
    this.emit("change");
  },    

  getState: function() {
    return {
      productItems: this.productItems
    };
  }
});

module.exports = store;

Désormais, les actions de produit, qui émettent la demande AJAX et, en cas de succès, déclenchent l'action LOAD_PRODUCTS_SUCCESS renvoyant les produits au magasin.

Actions du produit

var ProductClient = require("../fake-clients/product-client");

var actions = {

  loadProducts: function() {

    ProductClient.load(function(products) {
      this.dispatch(constants.LOAD_PRODUCTS_SUCCESS, {products: products});
    }.bind(this), function(error) {
      this.dispatch(constants.LOAD_PRODUCTS_FAIL, {error: error});
    }.bind(this));
  }    

};

module.exports = actions;

Donc, appeler this.getFlux().actions.productActions.loadProducts() à partir de tout composant écoutant ce magasin chargerait les produits. 

Vous pouvez imaginer différentes actions répondant aux interactions de l'utilisateur telles que addProduct(id)removeProduct(id) etc ... en suivant le même schéma.

J'espère que cet exemple aide un peu, car j'ai trouvé cela un peu difficile à mettre en œuvre, mais m'a certainement aidé à garder mes magasins 100% synchrones.

2
steven iseki

J'ai répondu ici à une question connexe: Comment gérer les appels api imbriqués en flux

Les actions ne sont pas censées causer des changements. Ils sont censés être comme un journal qui informe l'application d'un changement du monde extérieur, puis l'application répond à cette nouvelle. Les magasins provoquent des changements en eux-mêmes. Les actions ne font que les informer.

Bill Fisher, créateur de Flux https://stackoverflow.com/a/26581808/4258088

Ce que vous devriez fondamentalement faire, c’est d’énoncer via des actions les données dont vous avez besoin. Si le magasin est informé par l'action, il doit décider s'il doit extraire certaines données. 

Le magasin devrait être responsable de l'accumulation/de la récupération de toutes les données nécessaires. Il est important de noter cependant que, une fois que le magasin a demandé les données et obtenu la réponse, il doit déclencher lui-même une action avec les données extraites, contrairement au magasin qui gère/sauvegarde directement la réponse. 

Un magasin pourrait ressembler à quelque chose comme ceci:

class DataStore {
  constructor() {
    this.data = [];

    this.bindListeners({
      handleDataNeeded: Action.DATA_NEEDED,
      handleNewData: Action.NEW_DATA
    });
  }

  handleDataNeeded(id) {
    if(neededDataNotThereYet){
      api.data.fetch(id, (err, res) => {
        //Code
        if(success){
          Action.newData(payLoad);
        }
      }
    }
  }

  handleNewData(data) {
    //code that saves data and emit change
  }
}
2
MoeSattler

Voici mon point de vue sur ceci: http://www.thedreaming.org/2015/03/14/react-ajax/

J'espère que cela pourra aider. :)

0
Jason Walton