Javascript 在 Flux 应用程序中应该在哪里提出 ajax 请求?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/26632415/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-22 23:09:23  来源:igfitidea点击:

Where should ajax request be made in Flux app?

javascriptreactjsreactjs-flux

提问by Eniz Gülek

I'm creating a react.js application with flux architecture and I am trying figure out where and when a request for data from the server should be made. Is there a any example for this. (Not TODO app!)

我正在创建一个具有通量架构的 react.js 应用程序,我正在尝试弄清楚应该在何时何地从服务器请求数据。有没有这方面的例子。(不是 TODO 应用程序!)

采纳答案by Michelle Tilley

I'm a big proponent of putting async write operations in the action creators and async read operations in the store. The goal is to keep the store state modification code in fully synchronous action handlers; this makes them simple to reason about and simple to unit test. In order to prevent multiple simultaneous requests to the same endpoint (for example, double-reading), I'll move the actual request processing into a separate module that uses promises to prevent the multiple requests; for example:

我非常支持将异步写入操作放在操作创建器中并将异步读取操作放在存储中。目标是将商店状态修改代码保存在完全同步的动作处理程序中;这使得它们易于推理并且易于单元测试。为了防止对同一端点的多个同时请求(例如,双重读取),我将实际的请求处理移到一个单独的模块中,该模块使用 promise 来防止多个请求;例如:

class MyResourceDAO {
  get(id) {
    if (!this.promises[id]) {
      this.promises[id] = new Promise((resolve, reject) => {
        // ajax handling here...
      });
    } 
    return this.promises[id];
  }
}

While reads in the store involve asynchronous functions, there is an important caveat that the stores don't update themselves in the async handlers, but instead fire an action and onlyfire an action when the response arrives. Handlers for this action end up doing the actual state modification.

虽然 store 中的读取涉及异步函数,但有一个重要的警告,即 store 不会在异步处理程序中更新自己,而是触发一个动作并且在响应到达时触发一个动作。此操作的处理程序最终会进行实际的状态修改。

For example, a component might do:

例如,一个组件可能会:

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

The store would have a method implemented, perhaps, something like this:

商店会实现一个方法,也许是这样的:

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

回答by Sebastien Lorber

Fluxxor has an exampleof async communication with an API.

Fluxxor 有一个与 API 进行异步通信的示例

This blog posthas talks about it and has been featured on React's blog.

这篇博文对此进行了讨论,并在 React 的博客上进行了专题介绍。



I find this a very important and difficult question that is not clearly answered yet, as frontend software synchronization with the backend is still a pain.

我发现这是一个非常重要和困难的问题,目前还没有明确回答,因为前端软件与后端同步仍然很痛苦。

Should API requests be made in JSX components? Stores? Other place?

API 请求应该在 JSX 组件中进行吗?商店?其他地方?

Performing requests in stores mean that if 2 stores need the same data for a given action, they will issue 2 similar requets (unless you introduce dependencies between stores, which I really don't like)

在商店中执行请求意味着如果 2 个商店需要相同的数据来执行给定的操作,它们将发出 2 个类似的请求(除非您在商店之间引入依赖关系,我真的不喜欢

In my case, I have found this very handy to put Q promises as payload of actions because:

就我而言,我发现将 Q 承诺作为操作的有效载荷非常方便,因为:

  • My actions do not need to be serializable (I do not keep an event log, I don't need event replay feature of event sourcing)
  • It removes the need to have different actions/events (request fired/request completed/request failed) and have to match them using correlation ids when concurrent requests can be fired.
  • It permits to multiple store to listen to the completion of the same request, without introducing any dependency between the stores (however it may be better to introduce a caching layer?)
  • 我的操作不需要可序列化(我不保留事件日志,我不需要事件源的事件重播功能)
  • 它消除了对不同操作/事件(请求触发/请求完成/请求失败)的需要,并且在并发请求可以被触发时必须使用相关 ID 来匹配它们。
  • 它允许多个 store 监听同一个请求的完成,而不会在 store 之间引入任何依赖(但是引入缓存层可能更好?)


Ajax is EVIL

阿贾克斯是邪恶的

I think Ajax will be less and less used in the near future because it is very hard to reason about. The right way? Considering devices as part of the distributed system I don't know where I first came across this idea (maybe in this inspiring Chris Granger video).

我认为 Ajax 在不久的将来会越来越少使用,因为它很难推理。正确的方式?将设备视为分布式系统的一部分,我不知道我是从哪里第一次遇到这个想法的(也许是在这个鼓舞人心的 Chris Granger 视频中)。

Think about it. Now for scalability we use distributed systems with eventual consistency as storage engines (because we can't beat the CAP theoremand often we want to be available). These systems do not sync through polling each others (except maybe for consensus operations?) but rather use structures like CRDT and event logs to make all the members of the distributed system eventually consistent (members will converge to the same data, given enough time).

想想看。现在为了可扩展性,我们使用具有最终一致性的分布式系统作为存储引擎(因为我们无法击败CAP 定理,而且通常我们希望可用)。这些系统不会通过相互轮询来同步(除了共识操作?)而是使用像 CRDT 和事件日志这样的结构来使分布式系统的所有成员最终保持一致(如果有足够的时间,成员将收敛到相同的数据) .

Now think about what is a mobile device or a browser. It is just a member of the distributed system that may suffer of network latency and network partitionning.(ie you are using your smartphone on the subway)

现在想想什么是移动设备或浏览器。它只是可能遭受网络延迟和网络分区的分布式系统的一个成员。(即您在地铁上使用智能手机)

If we can build network partition and network speed tolerant databases (I mean we can still perform write operations to an isolated node), we can probably build frontend softwares (mobile or desktop) inspired by these concepts, that work well with offline mode supported out of the box without app features unavailability.

如果我们可以构建网络分区和网速容忍数据库(我的意思是我们仍然可以对隔离节点执行写操作),我们可能可以构建受这些概念启发的前端软件(移动或桌面),在支持离线模式的情况下运行良好没有应用程序功能不可用的框。

I think we should really inspire ourselves of how databases are working to architecture our frontend applications. One thing to notice is that these apps do not perform POST and PUT and GET ajax requests to send data to each others, but rather use event logs and CRDT to ensure eventual consistency.

我认为我们应该真正激励自己了解数据库是如何构建我们的前端应用程序的。需要注意的一件事是,这些应用程序不会执行 POST、PUT 和 GET ajax 请求来向彼此发送数据,而是使用事件日志和 CRDT 来确保最终的一致性。

So why not do that on the frontend? Notice that the backend is already moving in that direction, with tools like Kafka massively adopted by big players. This is somehow related to Event Sourcing / CQRS / DDD too.

那么为什么不在前端这样做呢?请注意,后端已经在朝着这个方向发展,像 Kafka 这样的工具被大玩家大量采用。这也与事件溯源/CQRS/DDD 有某种关系。

Check these awesome articles from Kafka authors to convince yourself:

查看 Kafka 作者的这些精彩文章以说服自己:

Maybe we can start by sending commands to the server, and receiving a stream of server events (through websockets for exemple), instead of firing Ajax requests.

也许我们可以先向服务器发送命令,然后接收服务器事件流(例如通过 websockets),而不是触发 Ajax 请求。

I have never been very comfortable with Ajax requests. As we React developpers tend to be functional programmers. I think it's hard to reason about local data that is supposed to be your "source of truth" of your frontend application, while the real source of truth is actually on the server database, and your "local" source of truth may already be outdated when you receive it, and will never converge to the real source of truth value unless you press some lame Refresh button... Is this engineering?

我从来没有对 Ajax 请求感到满意。我们 React 开发人员往往是函数式程序员。我认为很难推断本地数据应该是前端应用程序的“真实来源”,而真正的真实来源实际上在服务器数据库上,而你的“本地”真实来源可能已经过时了当你收到它时,除非你按下一些蹩脚的刷新按钮,否则永远不会收敛到真正的真值来源......这是工程吗?

However it's still a bit hard to design such a thing for some obvious reasons:

然而,由于一些明显的原因,设计这样的东西仍然有点困难:

  • Your mobile/browser client has limited resources and can not necessarily store all the data locally (thus sometimes requiring polling with an ajax request heavy content)
  • Your client should not see all the data of the distributed system so it requires somehow to filter the events that it receives for security reasons
  • 您的移动/浏览器客户端资源有限,不一定能在本地存储所有数据(因此有时需要使用 ajax 请求大量内容进行轮询)
  • 您的客户端不应看到分布式系统的所有数据,因此出于安全原因,它需要以某种方式过滤收到的事件

回答by fisherwebdev

You can call for data in either the action creators or the stores. The important thing is to not handle the response directly, but to create an action in the error/success callback. Handling the response directly in the store leads to a more brittle design.

您可以在操作创建器或存储中调用数据。重要的是不要直接处理响应,而是在错误/成功回调中创建一个动作。直接在 store 中处理响应会导致设计更加脆弱。

回答by svnm

I have been using Binary Muse's example from the Fluxxor ajax example. Here is my very simple example using the same approach.

我一直在使用Fluxxor ajax 示例中的Binary Muse示例。这是我使用相同方法的非常简单的示例。

I have a simple product storesome product actionsand the controller-viewcomponent which has sub-components that all respond to changes made to the product store. For instance product-slider, product-listand product-searchcomponents.

我有一个简单的产品存储一些产品操作控制器视图组件,其中包含所有响应对产品存储所做的更改的子组件。例如product-sliderproduct-listproduct-search组件。

Fake Product Client

假冒产品客户

Here is the fake client which you could substitute for calling an actual endpoint returning products.

这是您可以替代调用返回产品的实际端点的假客户端。

var ProductClient = {

  load: function(success, failure) {
    setTimeout(function() {
      var ITEMS = require('../data/product-data.js');
      success(ITEMS);
    }, 1000);
  }    
};

module.exports = ProductClient;

Product Store

产品商店

Here is the Product Store, obviously this is a very minimal store.

这是产品商店,显然这是一个非常小的商店。

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;

Now the product actions, which make the AJAX request and on success fire the LOAD_PRODUCTS_SUCCESS action returning products to the store.

现在发出 AJAX 请求并在成功时触发 LOAD_PRODUCTS_SUCCESS 操作将产品返回到商店的产品操作。

Product Actions

产品操作

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;

So calling this.getFlux().actions.productActions.loadProducts()from any component listening to this store would load the products.

因此,this.getFlux().actions.productActions.loadProducts()从任何监听此存储的组件调用都会加载产品。

You could imagine having different actions though which would respond to user interactions like addProduct(id)removeProduct(id)etc... following the same pattern.

你可以想象有不同的动作,但它们会响应用户交互addProduct(id)removeProduct(id)等......遵循相同的模式。

Hope that example helps a bit, as I found this a little tricky to implement, but certainly helped in keeping my stores 100% synchronous.

希望这个例子有点帮助,因为我发现这有点难以实现,但肯定有助于保持我的商店 100% 同步。

回答by MoeSattler

I answered a related question here: How to handle nested api calls in flux

我在这里回答了一个相关的问题:如何处理不断变化的嵌套 api 调用

Actions are not supposed to be things that cause a change. They are supposed to be like a newspaper that informs the application of a change in the outside world, and then the application responds to that news. The stores cause changes in themselves. Actions just inform them.

行动不应该是引起变化的事情。它们应该像一份报纸,通知应用程序外部世界的变化,然后应用程序响应该消息。商店本身会引起变化。行动只是通知他们。

Bill Fisher, creator of Flux https://stackoverflow.com/a/26581808/4258088

Bill Fisher,Flux 的创建者https://stackoverflow.com/a/26581808/4258088

What you basically should be doing is, stating via actions what data you need. If the store gets informed by the action, it should decide if it needs to fetch some data.

您基本上应该做的是,通过操作说明您需要什么数据。如果 store 收到 action 的通知,它应该决定是否需要获取一些数据。

The store should be responsible for accumulating/fetching all the needed data. It is important to note though, that after the store requested the data and gets the response, it should trigger an action itself with the fetched data, opposed to the store handling/saving the response directly.

存储应该负责累积/获取所有需要的数据。但需要注意的是,在 store 请求数据并获得响应之后,它应该使用获取的数据触发一个动作本身,而不是 store 直接处理/保存响应。

A stores could look like something like this:

商店可能看起来像这样:

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

回答by Jason Walton

Here's my take on this: http://www.thedreaming.org/2015/03/14/react-ajax/

这是我对此的看法:http: //www.thedreaming.org/2015/03/14/react-ajax/

Hope that helps. :)

希望有帮助。:)