Programming

Flux 앱에서 Ajax 요청은 어디에서해야합니까?

procodes 2020. 5. 12. 21:12
반응형

Flux 앱에서 Ajax 요청은 어디에서해야합니까?


플럭스 아키텍처로 react.js 응용 프로그램을 만들고 서버에서 데이터를 요청 해야하는 위치와 시간을 알아 내려고합니다. 이에 대한 예가 있습니까? (TODO 앱이 아닙니다!)


저는 액션 제작자에 비동기 쓰기 작업을 저장하고 상점에서 비동기 읽기 작업을 수행하는 데 큰 지지자입니다. 목표는 상점 상태 수정 코드를 완전히 동기식 조치 핸들러에 유지하는 것입니다. 따라서 추론하기 쉽고 단위 테스트가 간단합니다. 여러 개의 동시 요청을 동일한 엔드 포인트 (예 : 이중 판독)로 방지하기 위해 실제 요청 처리를 여러 요청을 방지하기 위해 약속을 사용하는 별도의 모듈로 옮길 것입니다. 예를 들면 다음과 같습니다.

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

상점의 읽기에는 비동기 함수가 포함되지만 상점이 비동기 핸들러에서 자체적으로 업데이트하지 않고 대신 조치가 실행되고 응답이 도착할 때만 조치가 실행된다는 중요한 경고 가 있습니다. 이 조치에 대한 핸들러는 실제 상태 수정을 수행합니다.

예를 들어, 구성 요소는 다음을 수행 할 수 있습니다.

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

상점은 아마도 다음과 같은 방법을 구현했을 것입니다.

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

Fluxxor에는 API와의 비동기 통신 예제 가 있습니다.

블로그 게시물 에 대해 이야기하고 React의 블로그에 실 렸습니다.


백엔드와의 프론트 엔드 소프트웨어 동기화가 여전히 고통스럽기 때문에 아직 명확하게 답변되지 않은 매우 중요하고 어려운 질문입니다.

JSX 컴포넌트에서 API 요청을해야합니까? 백화점? 다른 장소?

상점에서 요청을 수행한다는 것은 2 개의 상점이 주어진 조치에 대해 동일한 데이터를 필요로하는 경우 2 개의 유사한 요청을 발행한다는 것을 의미합니다 (상점 사이에 종속성을 도입하지 않는 한 실제로는 마음에 들지 않습니다 )

제 경우에는 Q 약속을 행동의 페이로드로 넣는 것이 매우 편리하다는 것을 알았습니다.

  • 내 작업은 직렬화 할 필요가 없습니다 (이벤트 로그를 보관하지 않고 이벤트 소싱의 이벤트 재생 기능이 필요하지 않습니다)
  • 서로 다른 조치 / 이벤트 (요청 발생 / 요청 완료 / 요청 실패)가 없어야하며 동시 요청이 발생 될 수있을 때 상관 관계 ID를 사용하여 일치해야합니다.
  • 여러 상점이 상점간에 종속성을 유발하지 않고 동일한 요청의 완료를 청취 할 수 있습니다 (그러나 캐싱 계층을 도입하는 것이 더 나을 수 있습니까?).

Ajax는 EVIL입니다

나는 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 theorem and 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).

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.

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.

Check these awesome articles from Kafka authors to convince yourself:

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.

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?

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

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.


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

I have a simple product store some product actions and the controller-view component which has sub-components that all respond to changes made to the product store. For instance product-slider, product-list and product-search components.

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.

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.

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

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


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

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

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.

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.

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

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

Hope that helps. :)

참고URL : https://stackoverflow.com/questions/26632415/where-should-ajax-request-be-made-in-flux-app

반응형