web-dev-qa-db-fra.com

Comment éviter l'envoi en milieu d'expédition

Dans mon Flux architectured React application je récupère des données d'un magasin, et je voudrais créer une action pour demander ces informations si elles n'existent pas. Cependant, je rencontre une erreur où le répartiteur expédie déjà.

Mon code souhaité est quelque chose comme:

getAll: function(options) {
  options = options || {};
  var key = JSON.stringify(options);
  var ratings = _data.ratings[key];

  if (!ratings) {
    RatingActions.fetchAll(options);
  }

  return ratings || [];
}

Toutefois, échoue par intermittence lorsque le répartiteur envoie déjà une action, avec le message Invariant Violation: Dispatch.dispatch(...): Cannot dispatch in the middle of a dispatch.. Je fais souvent des demandes en réponse à un changement d'état de l'application (par exemple, une plage de dates). Mon composant où je fais la demande, en réponse à un événement de modification de AppStore a les éléments suivants:

getStateFromStores: function() {
  var dateOptions = {
    startDate: AppStore.getStartISOString(),
    endDate: AppStore.getEndISOString()
  };

  return {
    ratings: RatingStore.getAll(dateOptions),
  };
},

Je suis conscient que le chaînage d'événements est un contre-modèle de Flux, mais je ne sais pas quelle architecture est meilleure pour récupérer des données quand elles n'existent pas encore. Actuellement, j'utilise ce terrible hack:

getAll: function(options) {
  options = options || {};
  var key = JSON.stringify(options);
  var ratings = _data.ratings[key];

  if (!ratings) {
    setTimeout(function() {
      if (!RatingActions.dispatcher.isDispatching()) {
        RatingActions.fetchAll(options);
      }
    }, 0);
  }

  return ratings || [];
},

Quelle serait une meilleure architecture, qui évite le chaînage d'événements ou l'erreur du répartiteur? Est-ce vraiment un enchaînement d'événements? Je veux juste changer les données en fonction des paramètres définis par l'application.

Merci!

20

Vous pouvez utiliser la fonction Flux waitFor() au lieu d'un setTimeout

Par exemple, vous avez 2 magasins enregistrés dans le même répartiteur et un magasin attend Pour que l'autre magasin traite d'abord l'action, puis celui qui attend peut se mettre à jour après et distribuer l'événement de modification. Voir Flux docs exemple

13
andykenward

Mon erreur particulière s'est produite parce que mes magasins ont émis leur événement de changement pendant l'envoi de l'action, alors qu'il parcourait toujours les écouteurs. Cela signifie que tous les écouteurs (c'est-à-dire les composants) qui ont ensuite déclenché une action en raison d'un changement de données dans le magasin interrompraient l'envoi. Je l'ai corrigé en émettant l'événement de modification une fois l'envoi terminé.

Donc ça:

this.emit(CHANGE_EVENT);

Est devenu

var self = this;

setTimeout(function() { // Run after dispatcher has finished
  self.emit(CHANGE_EVENT);
}, 0);

Encore un peu hacky (va probablement réécrire donc ne nécessite pas de setTimeout). Ouvert aux solutions qui répondent au problème architectural, plutôt qu'à ce détail d'implémentation.

6

La raison pour laquelle vous obtenez une répartition au milieu d'une répartition précédente est que votre magasin envoie une action (invoque un créateur d'action) de manière synchrone dans le gestionnaire pour une autre action. Le répartiteur envoie techniquement jusqu'à ce que tous ses rappels enregistrés aient été exécutés. Donc, si vous envoyez une nouvelle action à partir de l'un des rappels enregistrés, vous obtiendrez cette erreur.

Cependant, si vous effectuez un travail asynchrone, par exemple faire une demande ajax, vous pouvez toujours envoyer une action dans les rappels ajax, ou le rappel asynchrone en général. Cela fonctionne, car dès que la fonction asynchrone a été invoquée, elle continue par définition immédiatement l'exécution de la fonction et place le rappel dans la file d'attente des événements.

Comme l'a souligné Amida et dans les commentaires de cette réponse, c'est une question de choix de faire des demandes ajax depuis le magasin en réponse à une action, ou de le faire dans le magasin. La clé est qu'un magasin ne doit muter que son état en réponse à une action, pas dans un rappel ajax/async.

Dans votre cas particulier, cela serait illustré par quelque chose comme ceci pour le rappel enregistré de votre magasin, si vous préférez passer les appels ajax depuis le magasin:

onGetAll: function(options) {
    // ...do some work

    request(ajaxOptions) // example for some promise-based ajax lib
        .then(function(data) {
             getAllSuccessAction(data); // run after dispatch
        })
        .error(function(data) {
             getAllFailedAction(data); // run after dispatch
        });

     // this will be immediately run during getAllAction dispatch
     return this.state[options];
},
onGetAllSuccess: function(data) {
    // update state or something and then trigger change event, or whatever
},    
onGetAllFailed: function(data) {
    // handle failure somehow
}

Ou vous pouvez simplement mettre l'appel ajax dans votre créateur d'action et envoyer les actions "succès/échec" à partir de là.

5
Hannes Johansson

vous pouvez utiliser l'option "différer" dans le répartiteur. Dans votre cas, ce serait comme:

RatingActions.fetchAll.defer(options);
1
abhisekpaul

Dans mon cas, je récupère des données via les actions/créateurs d'actions. Le magasin n'est qu'un emplacement de vidage qui reçoit la charge utile d'une action.

Cela signifie que je "fetchall" dans une action, puis passer le résultat au magasin qui fera quoi que ce soit avec elle, puis émettra un événement de changement.

Certaines personnes envisagent d'utiliser des magasins comme moi, d'autres pensent comme vous. Certaines personnes sur Facebook utilisent "mon" approche:

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

Je pense que cela éviterait probablement le problème d'expédition en traitant vos magasins comme ça, mais je peux me tromper.

Une discussion intéressante est celle-ci: https://groups.google.com/forum/#!topic/reactjs/jBPHH4Q-8Sc

où Jing Chen (ingénieur Facebook) explique ce qu'elle pense de l'utilisation des magasins.

0
Kev