Je crée une application react.js avec une architecture de flux et j'essaie de savoir où et quand une demande de données du serveur doit être faite. Y a-t-il un exemple pour cela? (Pas l'application TODO!)
Je crée une application react.js avec une architecture de flux et j'essaie de savoir où et quand une demande de données du serveur doit être faite. Y a-t-il un exemple pour cela? (Pas l'application TODO!)
Réponses:
Je suis un grand partisan de la mise en place des opérations d'écriture asynchrone dans les créateurs d'actions et des 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. Afin d'empêcher plusieurs demandes simultanées vers le même point de terminaison (par exemple, double lecture), je déplacerai le traitement des demandes réelles dans un module distinct 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 existe une mise en garde importante selon laquelle les magasins ne se mettent pas à jour eux-mêmes dans les gestionnaires asynchrones, mais déclenchent plutôt une action et ne déclenchent une action que lorsque la réponse arrive. Les gestionnaires de cette action finissent par effectuer la modification d'état réelle.
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
}
}
flux
est injecté dans les magasins après la construction, donc pas de bon moyen d'obtenir des actions dans la méthode d'initialisation. Vous trouverez peut-être de bonnes idées dans les bibliothèques de flux isomorphes de Yahoo; c'est quelque chose que Fluxxor v2 devrait mieux supporter. N'hésitez pas à m'envoyer un e-mail si vous souhaitez en discuter davantage.
data: result
devrait être data : data
, non? il n'y a pas result
. peut-être préférable de renommer le paramètre de données en charge utile ou quelque chose comme ça.
Fluxxor propose un exemple de communication asynchrone avec une API.
Ce billet de blog en parle et a été présenté sur le blog de React.
Je trouve que c'est une question très importante et difficile à laquelle il n'est pas encore clairement répondu, car la synchronisation des logiciels frontaux avec le backend est toujours difficile.
Les demandes d'API doivent-elles être effectuées dans les composants JSX? Magasins? Autre endroit?
L'exécution de 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 requets similaires (sauf si vous introduisez des dépendances entre magasins, ce que je n'aime vraiment pas )
Dans mon cas, j'ai trouvé cela très pratique pour mettre les promesses Q comme charge utile d'actions parce que:
Ajax est EVIL
Je pense que l'Ajax sera de moins en moins utilisé dans un futur proche car c'est très difficile à raisonner. Le droit chemin? Considérant les appareils comme faisant partie du système distribué, je ne sais pas d'où je suis tombé sur cette idée (peut-être dans cette vidéo inspirante de Chris Granger ).
Pensez-y. Maintenant, pour l'évolutivité, nous utilisons des systèmes distribués avec une cohérence éventuelle comme moteurs de stockage (parce que nous ne pouvons pas battre le théorème CAP et souvent nous voulons être disponibles). Ces systèmes ne se synchronisent pas en s'interrogeant mutuellement (sauf peut-être pour des opérations de consensus?) Mais utilisent plutôt des structures comme CRDT et les 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) .
Pensez maintenant à ce qu'est un appareil mobile ou un navigateur. Il s'agit simplement d'un membre du système distribué qui peut souffrir de latence du réseau et de partitionnement du réseau. (c'est-à-dire que vous utilisez votre smartphone dans le métro)
Si nous pouvons créer des bases de données de partition réseau et de tolérance de vitesse réseau (je veux dire que 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 ligne pris en charge. de la boîte sans indisponibilité des fonctionnalités de l'application.
Je pense que nous devrions vraiment nous inspirer du fonctionnement des bases de données pour l'architecture de nos applications frontales. Une chose à noter est que ces applications n'effectuent pas de requêtes POST et PUT et GET ajax pour s'envoyer des données, mais utilisent plutôt des journaux d'événements et CRDT pour assurer une cohérence éventuelle.
Alors pourquoi ne pas faire ça sur le frontend? Notez que le backend évolue déjà dans cette direction, avec des outils comme Kafka massivement adoptés par les grands acteurs. Cela est également lié à Event Sourcing / CQRS / DDD.
Consultez ces articles impressionnants des auteurs de Kafka pour vous convaincre:
Peut-être que nous pouvons commencer par envoyer des commandes au serveur et recevoir un flux d'événements du serveur (via des websockets par exemple), au lieu de lancer des requêtes Ajax.
Je n'ai jamais été très à l'aise avec les demandes Ajax. Au fur et à mesure que nous réagissons, les développeurs ont tendance à être des programmeurs fonctionnels. Je pense qu'il est difficile de raisonner sur les données locales qui sont censées être votre "source de vérité" de votre application frontale, alors que la véritable source de vérité est en fait sur la base de données du serveur, et votre source de vérité "locale" peut déjà être dépassée lorsque vous le recevez, et ne convergera jamais vers la vraie source de valeur de vérité à moins d'appuyer 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:
this.dispatch("LOAD_DATA", {dataPromise: yourPromiseHere});
Vous pouvez appeler des données dans les créateurs d'actions ou dans les magasins. L'important est de ne pas gérer directement la réponse, mais de créer une action dans le rappel d'erreur / succès. La gestion de la réponse directement dans le magasin conduit à une conception plus fragile.
J'utilise l'exemple de Binary Muse de l'exemple Fluxxor ajax . Voici mon exemple très simple utilisant la même approche.
J'ai un magasin de produits simple , certaines actions de produit et le composant de vue de contrôleur qui a des sous-composants qui répondent tous aux modifications apportées au magasin de produits . Par exemple , les composants product-slider , product-list et product-search .
Client de faux produits
Voici le faux client que vous pourriez remplacer pour appeler un point de terminaison réel 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, c'est évidemment 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;
Maintenant, les actions de produit, qui effectuent 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 n'importe quel composant écoutant ce magasin chargerait les produits.
Vous pourriez imaginer avoir différentes actions qui répondraient aux interactions des utilisateurs comme addProduct(id)
removeProduct(id)
etc ... en suivant le même modèle.
J'espère que cet exemple aide un peu, car j'ai trouvé cela un peu délicat à mettre en œuvre, mais certainement aidé à garder mes magasins 100% synchrones.
J'ai répondu à une question connexe ici: comment gérer les appels d'API imbriqués dans le flux
Les actions ne sont pas censées être des choses qui provoquent un changement. Ils sont censés être comme un journal qui informe l'application d'un changement dans le monde extérieur, puis l'application répond à cette nouvelle. Les magasins provoquent des changements en eux-mêmes. Les actions les informent simplement.
Bill Fisher, créateur de Flux https://stackoverflow.com/a/26581808/4258088
Ce que vous devriez essentiellement faire, c'est indiquer, 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 récupérer des 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 qu'après que le magasin a demandé les données et obtenu la réponse, il devrait déclencher lui-même une action avec les données extraites, contrairement au magasin qui gère / enregistre 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
}
}
Voici mon point de vue à ce sujet: http://www.thedreaming.org/2015/03/14/react-ajax/
J'espère que cela pourra aider. :)