Estou criando um aplicativo react.js com arquitetura de fluxo e estou tentando descobrir onde e quando uma solicitação de dados do servidor deve ser feita. Existe algum exemplo para isso. (Não aplicativo TODO!)
Estou criando um aplicativo react.js com arquitetura de fluxo e estou tentando descobrir onde e quando uma solicitação de dados do servidor deve ser feita. Existe algum exemplo para isso. (Não aplicativo TODO!)
Respostas:
Sou um grande defensor de colocar operações de gravação assíncrona nos criadores de ação e operações de leitura assíncrona na loja. O objetivo é manter o código de modificação do estado da loja em manipuladores de ação totalmente síncronos; isso os torna simples de raciocinar e simples de testar na unidade. Para impedir várias solicitações simultâneas para o mesmo ponto de extremidade (por exemplo, leitura dupla), moverei o processamento real da solicitação para um módulo separado que usa promessas para impedir as diversas solicitações; por exemplo:
class MyResourceDAO {
get(id) {
if (!this.promises[id]) {
this.promises[id] = new Promise((resolve, reject) => {
// ajax handling here...
});
}
return this.promises[id];
}
}
Enquanto as leituras na loja envolvem funções assíncronas, há uma ressalva importante de que as lojas não se atualizam nos manipuladores assíncronos, mas, em vez disso, acionam uma ação e somente acionam quando a resposta chega. Os manipuladores dessa ação acabam realizando a modificação do estado.
Por exemplo, um componente pode fazer:
getInitialState() {
return { data: myStore.getSomeData(this.props.id) };
}
A loja teria um método implementado, talvez, algo como isto:
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
é injetado nas lojas após a construção, portanto, não há uma ótima maneira de obter ações no método de inicialização. Você pode encontrar boas idéias nas bibliotecas de fluxo isomoróficas do Yahoo; isso é algo que o Fluxxor v2 deve suportar melhor. Sinta-se à vontade para me enviar um e-mail se quiser conversar mais sobre isso.
data: result
deveria ser data : data
, certo? não existe result
. talvez seja melhor renomear os parâmetros de dados para carga útil ou algo assim.
O Fluxxor tem um exemplo de comunicação assíncrona com uma API.
Esta postagem do blog fala sobre isso e foi destaque no blog da React.
Acho essa uma pergunta muito importante e difícil que ainda não foi respondida claramente, pois a sincronização do software front-end com o back-end ainda é um problema.
As solicitações de API devem ser feitas nos componentes JSX? Lojas? Outro lugar?
A execução de solicitações nas lojas significa que, se duas lojas precisarem dos mesmos dados para uma determinada ação, elas emitirão 2 requets semelhantes (a menos que você introduza dependências entre lojas, das quais eu realmente não gosto )
No meu caso, achei muito útil colocar as promessas de Q como carga útil de ações porque:
Ajax é MAU
Eu acho que o Ajax será cada vez menos usado no futuro próximo, porque é muito difícil argumentar. O caminho certo? Considerando os dispositivos como parte do sistema distribuído, não sei onde me deparei com essa ideia (talvez neste inspirador vídeo de Chris Granger ).
Pense nisso. Agora, para escalabilidade, usamos sistemas distribuídos com consistência eventual como mecanismos de armazenamento (porque não podemos vencer o teorema do CAP e muitas vezes queremos estar disponíveis). Esses sistemas não sincronizam através da pesquisa entre si (exceto talvez para operações de consenso?), Mas usam estruturas como CRDT e logs de eventos para tornar todos os membros do sistema distribuído eventualmente consistentes (os membros convergirão para os mesmos dados, com tempo suficiente) .
Agora pense no que é um dispositivo móvel ou um navegador. É apenas um membro do sistema distribuído que pode sofrer latência e particionamento de rede.(ou seja, você está usando seu smartphone no metrô)
Se pudermos criar partição de rede e bancos de dados tolerantes à velocidade da rede (quero dizer, ainda podemos executar operações de gravação em um nó isolado), provavelmente podemos criar softwares de front-end (móveis ou desktop) inspirados nesses conceitos, que funcionam bem com o modo offline suportado da caixa sem o aplicativo apresenta a indisponibilidade.
Acho que deveríamos nos inspirar em como os bancos de dados estão trabalhando para arquitetar nossos aplicativos de front-end. Uma coisa a observar é que esses aplicativos não executam solicitações POST e PUT e GET ajax para enviar dados uns aos outros, mas usam logs de eventos e CRDT para garantir consistência eventual.
Então, por que não fazer isso no frontend? Observe que o back-end já está se movendo nessa direção, com ferramentas como Kafka adotadas massivamente por grandes jogadores. De alguma forma, isso também está relacionado ao Event Sourcing / CQRS / DDD.
Confira estes artigos impressionantes dos autores Kafka para se convencer:
Talvez possamos começar enviando comandos para o servidor e recebendo um fluxo de eventos do servidor (por meio de websockets, por exemplo), em vez de disparar solicitações do Ajax.
Nunca me senti muito à vontade com os pedidos do Ajax. À medida que reagimos, os desenvolvedores tendem a ser programadores funcionais. Eu acho que é difícil argumentar sobre dados locais que deveriam ser sua "fonte de verdade" do seu aplicativo de front-end, enquanto a fonte real de verdade está realmente no banco de dados do servidor, e sua fonte "local" de verdade já pode estar desatualizada quando você o recebe e nunca convergirá para a fonte real do valor da verdade, a menos que você pressione algum botão de atualização coxo ... Isso é engenharia?
No entanto, ainda é um pouco difícil projetar isso por alguns motivos óbvios:
this.dispatch("LOAD_DATA", {dataPromise: yourPromiseHere});
Você pode solicitar dados nos criadores da ação ou nas lojas. O importante é não manipular a resposta diretamente, mas criar uma ação no retorno de chamada de erro / êxito. O tratamento da resposta diretamente na loja leva a um design mais frágil.
Eu tenho usado o exemplo do Binary Muse no exemplo do Fluxxor ajax . Aqui está o meu exemplo muito simples, usando a mesma abordagem.
Tenho uma loja de produtos simples , algumas ações do produto e o componente de visualização do controlador, que possui subcomponentes que respondem a alterações feitas na loja de produtos . Por exemplo produto-slider , produto-list e produto de busca componentes.
Cliente de produto falso
Aqui está o cliente falso que você pode substituir por chamar um endpoint real que retorne produtos.
var ProductClient = {
load: function(success, failure) {
setTimeout(function() {
var ITEMS = require('../data/product-data.js');
success(ITEMS);
}, 1000);
}
};
module.exports = ProductClient;
Loja de produtos
Aqui está a loja do produto, obviamente essa é uma loja muito mínima.
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;
Agora, as ações do produto, que fazem a solicitação do AJAX e, com sucesso, acionam a ação LOAD_PRODUCTS_SUCCESS que retorna produtos para a loja.
Ações do produto
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;
Então chamando this.getFlux().actions.productActions.loadProducts()
de qualquer componente que esteja ouvindo esta loja carregaria os produtos.
Você pode imaginar ações diferentes, que responderiam às interações do usuário como addProduct(id)
removeProduct(id)
etc ... seguindo o mesmo padrão.
Espero que o exemplo ajude um pouco, pois achei isso um pouco complicado de implementar, mas certamente ajudou a manter minhas lojas 100% síncronas.
Respondi a uma pergunta relacionada aqui: Como lidar com chamadas de API aninhadas no fluxo
As ações não devem ser coisas que causam uma mudança. Eles deveriam ser como um jornal que informa a aplicação de uma mudança no mundo exterior e, em seguida, o aplicativo responde a essas notícias. As lojas causam mudanças em si mesmas. Ações apenas os informam.
Bill Fisher, criador do Flux https://stackoverflow.com/a/26581808/4258088
Basicamente, o que você deve fazer é declarar por meio de ações quais dados você precisa. Se a loja for informada pela ação, deve decidir se precisa buscar alguns dados.
A loja deve ser responsável por acumular / buscar todos os dados necessários. É importante observar, porém, que depois que a loja solicitou os dados e obtém a resposta, ela deve disparar uma ação com os dados buscados, em oposição à loja que manipula / salva a resposta diretamente.
A lojas poderia ser algo como isto:
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
}
}
Aqui está minha opinião sobre isso: http://www.thedreaming.org/2015/03/14/react-ajax/
Espero que ajude. :)