Onde deve ser feito o pedido ajax no aplicativo Flux?

Estou criando um aplicativo react.js com arquitetura de stream e estou tentando descobrir onde e quando um pedido de dados do servidor deve ser feito. Existe algum exemplo disso? (Não TODO app!)

Sou um grande defensor de colocar operações de gravação assíncrona nos criadores de ações e operações de leitura assíncrona na loja. O objective é manter o código de modificação do estado da loja em manipuladores de ações totalmente síncronas; isso os torna simples de raciocinar e simples de testar na unidade. Para evitar várias solicitações simultâneas para o mesmo terminal (por exemplo, leitura dupla), moverei o processamento de solicitação real para um módulo separado que use promises para impedir as várias 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 asynchronouss, mas acionam uma ação e triggersm uma ação quando a resposta chega. Os manipuladores dessa ação acabam fazendo a modificação do estado real.

Por exemplo, um componente pode fazer:

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

A loja teria um método implementado, talvez, algo assim:

 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 } } 

O Fluxxor tem um exemplo de comunicação assíncrona com uma API.

Este post do blog fala sobre isso e foi apresentado no blog do React.


Eu acho isso uma questão muito importante e difícil que ainda não está claramente respondida, já que a synchronization do software frontend com o backend ainda é uma dor.

As solicitações da API devem ser feitas nos componentes JSX? Lojas? Outro lugar?

Realizar pedidos nas lojas significa que se duas lojas precisarem dos mesmos dados para uma determinada ação, elas emitirão duas requisições semelhantes (a menos que você introduza dependencies entre as lojas, o que eu realmente não gosto )

No meu caso, achei muito útil colocar promises de Q como carga de ações porque:

  • Minhas ações não precisam ser serializáveis ​​(não mantenho um log de events, não preciso do recurso de repetição de events de fornecimento de events)
  • Isso elimina a necessidade de ter ações / events diferentes (solicitação triggersda / solicitação concluída / solicitação com falha) e ter que corresponder usando IDs de correlação quando solicitações simultâneas podem ser triggersdas.
  • Ele permite que várias lojas ouçam a conclusão da mesma solicitação, sem introduzir qualquer dependência entre as lojas (no entanto, pode ser melhor introduzir uma camada de armazenamento em cache?)

Ajax é MAL

Eu acho que o Ajax será cada vez menos usado no futuro próximo, porque é muito difícil de raciocinar. O caminho certo? Considerando dispositivos como parte do sistema distribuído, eu não sei onde eu encontrei essa idéia pela primeira vez (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 CAP e geralmente queremos estar disponíveis). Esses sistemas não são sincronizados através de sondagens entre si (exceto talvez para operações de consenso?), Mas usam estruturas como CRDT e logs de events para tornar consistentes todos os membros do sistema distribuído (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 de latência de rede e particionamento de rede. (ou seja, você está usando seu smartphone no metrô)

Se pudermos criar bancos de dados tolerantes à velocidade de rede e de partição de rede (quero dizer, ainda podemos executar operações de gravação em um nó isolado), provavelmente poderemos criar softwares frontend (móveis ou desktop) inspirados nesses conceitos, que funcionam bem com o modo offline da checkbox sem resources do aplicativo indisponibilidade.

Acho que deveríamos realmente nos inspirar em como os bancos de dados estão trabalhando para arquitetar nossos aplicativos frontend. Uma coisa a notar é que esses aplicativos não executam solicitações POST e PUT e GET ajax para enviar dados uns aos outros, mas usam logs de events e CRDT para garantir consistência eventual.

Então, por que não fazer isso no frontend? Observe que o backend já está se movendo nessa direção, com ferramentas como o Kafka adotado maciçamente pelos grandes jogadores. Isto está de alguma forma relacionado com o Event Sourcing / CQRS / DDD também.

Confira esses artigos incríveis dos autores do Kafka para se convencer:

  • PROCESSAMENTO DE FLUXO, FONTE DE EVENTOS, REATIVO, CEP … E FAZENDO SENTIDO DE TODOS
  • O Log: O que todo engenheiro de software deve saber sobre a abstração unificadora dos dados em tempo real .

Talvez possamos começar enviando comandos para o servidor e recebendo um stream de events do servidor (por meio de websockets, por exemplo), em vez de triggersr solicitações Ajax.

Eu nunca me senti muito confortável com os pedidos do Ajax. À medida que reagimos, os desenvolvedores tendem a ser programadores funcionais. Acho difícil raciocinar sobre dados locais que deveriam ser sua “fonte de verdade” de seu aplicativo de front-end, enquanto a verdadeira fonte de verdade está realmente no database do servidor, e sua fonte “local” de verdade já pode estar desatualizada quando você recebe, e nunca irá convergir para a verdadeira fonte do valor da verdade, a menos que você pressione algum botão Refresque … Isso é engenharia?

No entanto, ainda é um pouco difícil criar uma coisa dessas por algumas razões óbvias:

  • Seu cliente de navegador / móvel tem resources limitados e não pode necessariamente armazenar todos os dados localmente (portanto, às vezes, requerendo pesquisa com um conteúdo pesado de solicitação de ajax)
  • Seu cliente não deve ver todos os dados do sistema distribuído, portanto, ele precisa, de alguma maneira, filtrar os events que recebe por motivos de segurança

Você pode ligar para dados nos criadores de ações ou nas lojas. O importante é não manipular a resposta diretamente, mas criar uma ação no retorno de chamada de erro / sucesso. O manuseio da resposta diretamente na loja leva a um design mais frágil.

Eu tenho usado o exemplo da Binary Muse a partir do exemplo do Fluxxor ajax . Aqui está o meu exemplo muito simples usando a mesma abordagem.

Eu tenho um armazenamento de produto simples , algumas ações do produto e o componente de visão do controlador que tem subcomponentes que respondem às alterações feitas no armazenamento do produto . Por exemplo , controles deslizantes de produto, lista de produtos e componentes de pesquisa de produto .

Cliente do produto falsificado

Aqui está o cliente falso que você pode replace por chamar um ponto de extremidade real retornando 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 de produtos, obviamente, esta é 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 com que a solicitação do AJAX e o sucesso disparem a ação LOAD_PRODUCTS_SUCCESS, retornando 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; 

Portanto, chamar this.getFlux().actions.productActions.loadProducts() de qualquer componente que estivesse ouvindo esse armazenamento iria carregar os produtos.

Você poderia imaginar ter ações diferentes que responderiam a 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 difícil de implementar, mas certamente ajudei a manter minhas lojas 100% síncronas.

Eu respondi uma pergunta relacionada aqui: Como lidar com chamadas api aninhadas em stream

As ações não devem ser coisas que causam uma mudança. Eles devem ser como um jornal que informa a aplicação de uma mudança no mundo exterior e, em seguida, o aplicativo responde a essa notícia. As lojas causam mudanças em si mesmas. Ações apenas informá-los.

Bill Fisher, criador do Flux https://stackoverflow.com/a/26581808/4258088

O que você basicamente deveria estar fazendo é declarar através das ações quais dados você precisa. Se a loja for informada pela ação, ela 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 acionar uma ação com os dados buscados, em oposição ao armazenamento da manipulação / salvamento da resposta diretamente.

As lojas podem se parecer com algo assim:

 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. 🙂