Sto creando un'applicazione React.js con architettura Flux e sto cercando di capire dove e quando una richiesta di dati dal server dovrebbe essere fatta. C'è un esempio per questo. (Non l'app TODO!)
Sto creando un'applicazione React.js con architettura Flux e sto cercando di capire dove e quando una richiesta di dati dal server dovrebbe essere fatta. C'è un esempio per questo. (Non l'app TODO!)
Risposte:
Sono un grande sostenitore di mettere operazioni di scrittura asincrona nei creatori di azioni e operazioni di lettura asincrona nel negozio. L'obiettivo è mantenere il codice di modifica dello stato dell'archivio in gestori di azioni completamente sincroni; questo li rende semplici da ragionare e semplici da testare. Al fine di impedire più richieste simultanee allo stesso endpoint (ad esempio, doppia lettura), sposterò l'elaborazione della richiesta effettiva in un modulo separato che utilizza le promesse per impedire le richieste multiple; per esempio:
class MyResourceDAO {
get(id) {
if (!this.promises[id]) {
this.promises[id] = new Promise((resolve, reject) => {
// ajax handling here...
});
}
return this.promises[id];
}
}
Mentre si legge nel negozio coinvolgono funzioni asincrone, v'è un avvertimento importante che i negozi non si aggiornano nei gestori asincroni, ma invece sparano un'azione e solo fuoco un'azione quando la risposta arriva. I gestori di questa azione finiscono per apportare la modifica dello stato effettivo.
Ad esempio, un componente potrebbe fare:
getInitialState() {
return { data: myStore.getSomeData(this.props.id) };
}
Il negozio avrebbe implementato un metodo, forse, qualcosa del genere:
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
è stato iniettato nei negozi dopo la costruzione, quindi non c'è un ottimo modo per ottenere azioni nel metodo di inizializzazione. Potresti trovare alcune buone idee dalle librerie di flusso isomoropico di Yahoo; questo è qualcosa che Fluxxor v2 dovrebbe supportare meglio. Sentitevi liberi di inviarmi un'e-mail se volete chattare di più su questo.
data: result
dovrebbe essere data : data
vero? non c'è result
. forse meglio rinominare il parametro di dati in payload o qualcosa del genere.
Fluxxor ha un esempio di comunicazione asincrona con un'API.
Questo post sul blog ne parla ed è stato pubblicato sul blog di React.
Trovo che questa sia una domanda molto importante e difficile a cui non è ancora stata data una risposta chiara, poiché la sincronizzazione del software front-end con il back-end è ancora un problema.
Le richieste API devono essere fatte nei componenti JSX? I negozi? Un altro posto?
L'esecuzione delle richieste nei negozi significa che se 2 negozi necessitano degli stessi dati per una determinata azione, emetteranno 2 requisiti simili (a meno che non si introducano dipendenze tra negozi, cosa che non mi piace davvero )
Nel mio caso, l'ho trovato molto utile per mettere le promesse Q come payload di azioni perché:
Ajax è MALE
Penso che l'Ajax sarà sempre meno utilizzato nel prossimo futuro perché è molto difficile ragionare. Il modo giusto? Considerando i dispositivi come parte del sistema distribuito, non so dove mi sono imbattuto per la prima volta in questa idea (forse in questo video ispiratore di Chris Granger ).
Pensaci. Ora per la scalabilità utilizziamo sistemi distribuiti con eventuale coerenza come motori di archiviazione (perché non possiamo battere il teorema di CAP e spesso vogliamo essere disponibili). Questi sistemi non si sincronizzano tramite il polling reciproco (tranne forse per le operazioni di consenso?), Ma piuttosto utilizzano strutture come CRDT e registri eventi per rendere tutti i membri del sistema distribuito eventualmente coerenti (i membri convergeranno agli stessi dati, con un tempo sufficiente) .
Ora pensa a cosa è un dispositivo mobile o un browser. È solo un membro del sistema distribuito che può soffrire di latenza di rete e partizionamento di rete. (cioè stai usando il tuo smartphone in metropolitana)
Se siamo in grado di costruire database di partizione di rete e tolleranti della velocità di rete (intendo che possiamo ancora eseguire operazioni di scrittura su un nodo isolato), probabilmente possiamo costruire software di frontend (mobile o desktop) ispirati a questi concetti, che funzionano bene con la modalità offline supportata della confezione senza l'app non è disponibile.
Penso che dovremmo davvero ispirarci su come i database stanno lavorando per l'architettura delle nostre applicazioni frontend. Una cosa da notare è che queste app non eseguono le richieste ajax POST e PUT e GET per inviare dati l'un l'altro, ma piuttosto utilizzano i registri eventi e CRDT per garantire la coerenza finale.
Quindi perché non farlo sul frontend? Si noti che il backend si sta già muovendo in quella direzione, con strumenti come Kafka ampiamente adottati da grandi giocatori. Questo è in qualche modo legato anche a Event Sourcing / CQRS / DDD.
Dai un'occhiata a questi fantastici articoli degli autori di Kafka per convincerti:
Forse potremmo iniziare inviando comandi al server e ricevendo un flusso di eventi del server (tramite i websocket per esempio), invece di lanciare richieste Ajax.
Non mi sono mai trovato a mio agio con le richieste dell'Ajax. Mentre reagiamo, gli sviluppatori tendono ad essere programmatori funzionali. Penso che sia difficile ragionare sui dati locali che dovrebbero essere la tua "fonte di verità" della tua applicazione frontend, mentre la vera fonte di verità è in realtà sul database del server, e la tua fonte di "verità" locale potrebbe essere già obsoleta quando lo ricevi, e non convergeranno mai nella vera fonte del valore della verità a meno che tu non prema un po 'il pulsante di aggiornamento zoppo ... È questa ingegneria?
Tuttavia è ancora un po 'difficile progettare una cosa del genere per alcuni ovvi motivi:
this.dispatch("LOAD_DATA", {dataPromise: yourPromiseHere});
Puoi chiamare i dati nei creatori dell'azione o nei negozi. L'importante è non gestire direttamente la risposta, ma creare un'azione nel callback errore / successo. Gestire la risposta direttamente nel negozio porta a un design più fragile.
Ho usato l'esempio di Binary Muse dall'esempio ajax di Fluxxor . Ecco il mio esempio molto semplice usando lo stesso approccio.
Ho un semplice negozio di prodotti con alcune azioni del prodotto e il componente di visualizzazione del controller che ha componenti secondari che rispondono tutti alle modifiche apportate al negozio di prodotti . Per esempio product-cursore , prodotto-list e prodotto di ricerca componenti.
Cliente prodotto falso
Ecco il client falso che è possibile sostituire per chiamare un endpoint effettivo che restituisce prodotti.
var ProductClient = {
load: function(success, failure) {
setTimeout(function() {
var ITEMS = require('../data/product-data.js');
success(ITEMS);
}, 1000);
}
};
module.exports = ProductClient;
Negozio di prodotti
Ecco il Product Store, ovviamente questo è un negozio molto minimale.
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;
Ora le azioni del prodotto, che fanno la richiesta AJAX e in caso di successo attivano l'azione LOAD_PRODUCTS_SUCCESS che restituisce i prodotti al negozio.
Azioni sul prodotto
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;
Pertanto, la chiamata this.getFlux().actions.productActions.loadProducts()
da qualsiasi componente che ascolta questo negozio carica i prodotti.
Potresti immaginare di avere azioni diverse che potrebbero rispondere alle interazioni dell'utente come addProduct(id)
removeProduct(id)
etc ... seguendo lo stesso schema.
Spero che questo esempio aiuti un po ', dato che l'ho trovato un po' difficile da implementare, ma sicuramente ha aiutato a mantenere i miei negozi sincronizzati al 100%.
Ho risposto a una domanda correlata qui: Come gestire le chiamate API annidate nel flusso
Le azioni non dovrebbero essere cose che causano un cambiamento. Dovrebbero essere come un giornale che informa l'applicazione di un cambiamento nel mondo esterno, e quindi l'applicazione risponde a quella notizia. I negozi causano cambiamenti in se stessi. Le azioni li informano e basta.
Bill Fisher, creatore di Flux https://stackoverflow.com/a/26581808/4258088
Quello che dovresti sostanzialmente fare è, indicando tramite azioni quali dati ti servono. Se il negozio viene informato dall'azione, dovrebbe decidere se deve recuperare alcuni dati.
Il negozio dovrebbe essere responsabile dell'accumulo / recupero di tutti i dati necessari. È importante notare, tuttavia, che dopo che il negozio ha richiesto i dati e ottenuto la risposta, dovrebbe innescare un'azione stessa con i dati recuperati, al contrario del negozio che gestisce / salva direttamente la risposta.
Un negozio potrebbe assomigliare a questo:
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
}
}
Ecco la mia opinione su questo: http://www.thedreaming.org/2015/03/14/react-ajax/
Spero che aiuti. :)