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


194

플럭스 아키텍처로 react.js 응용 프로그램을 만들고 있는데 서버에서 데이터 요청을 언제 어디서 수행해야하는지 파악하려고합니다. 이에 대한 예가 있습니까? (TODO 앱이 아닙니다!)

답변:


127

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

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

액션 페이로드 안에 약속을 넣으려고 했습니까? 나는 그것이 더 쉽게보다 파견 여러 작업을 처리하기 위해 찾을
세바 Lorber

플럭스에 큰 무승부 @SebastienLorber 나 명시 적 동기 코드 경로에있는 모든 상태 업데이트를 유지하고 위해 단지 내가 상점 내부 비동기를 피할 수 있도록, 액션 파견의 결과로.
Michelle Tilley 2013 년

1
@Federico 아직도 "최고의"솔루션이 무엇인지 명확하지 않습니다. 미해결 비동기 요청 수를 계산하는 것과 함께 데이터로드를위한이 전략을 실험 해 왔습니다. 불행히도 flux시공 후에 상점에 주입되므로 initialize 메소드에서 조치를 취할 수있는 좋은 방법이 없습니다. Yahoo의 isomorophic flux libs에서 좋은 아이디어를 찾을 수 있습니다. 이것이 Fluxxor v2가 더 잘 지원해야하는 것입니다. 이것에 대해 더 많은 대화를 원하시면 언제든지 저에게 이메일을 보내주십시오.
Michelle Tilley

1
data: result이어야 data : data합니까? 없습니다 result. 데이터 매개 변수의 이름을 페이로드 또는 이와 유사한 것으로 바꾸는 것이 좋습니다.
oligofren

2
이 오래된 스레드 는 특히 Bill Fisher와 Jing Chen의 의견에 매우 유용 하다는 것을 알았습니다 . 이것은 @BinaryMuse가 액션 제작자에서 디스패치가 발생한다는 사소한 차이로 제안하는 것과 매우 가깝습니다.
phillipwei

37

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

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


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

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

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

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

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

Ajax는 EVIL입니다

나는 Ajax가 추론하기가 매우 어렵 기 때문에 가까운 미래에 점점 더 적게 사용될 것이라고 생각합니다. 옳은 길? 분산 시스템의 일부로 장치를 고려할 때이 아이디어를 처음 발견 한 곳을 알지 못합니다 (이 영감을주는 Chris Granger 비디오에서 ).

생각 해봐 이제 확장 성을 위해 스토리지 엔진으로서 최종 일관성을 갖춘 분산 시스템을 사용합니다 ( CAP 정리를 이길 수없고 종종 사용 가능하기를 원하기 때문에). 이러한 시스템은 서로의 폴링을 통해 동기화되지 않고 (합의 작업 제외) 분산 시스템의 모든 구성원을 일관되게 만들기 위해 CRDT 및 이벤트 로그와 같은 구조를 사용합니다 (구성원은 충분한 시간이 주어지면 동일한 데이터로 수렴됩니다) .

이제 모바일 장치 나 브라우저가 무엇인지 생각해보십시오. 네트워크 대기 시간 및 네트워크 파티셔닝이 발생할 수있는 분산 시스템의 구성원 일뿐입니다. (즉, 지하철에서 스마트 폰을 사용하고 있습니다)

네트워크 파티션 및 네트워크 속도 허용 데이터베이스를 구축 할 수 있다면 (여전히 고립 된 노드에 대한 쓰기 작업을 수행 할 수 있음) 이러한 개념에서 영감을 얻은 프런트 엔드 소프트웨어 (모바일 또는 데스크탑)를 구축 할 수 있습니다. 응용 프로그램 기능을 사용할 수없는 상자의.

데이터베이스가 프론트 엔드 응용 프로그램을 아키텍처 화하는 방법에 대해 영감을 얻어야합니다. 주목할 점은 이러한 앱이 서로에게 데이터를 전송하기 위해 POST 및 PUT 및 GET ajax 요청을 수행하지 않고 이벤트 로그 및 CRDT를 사용하여 최종 일관성을 유지한다는 것입니다.

왜 프론트 엔드에서 그렇게하지 않습니까? 백엔드가 이미 그 방향으로 움직이고 있으며 Kafka와 같은 도구는 큰 플레이어가 대규모로 채택하고 있습니다. 이것은 어떻게 든 이벤트 소싱 / CQRS / DDD와 관련이 있습니다.

Kafka 작가의 멋진 기사를 확인하여 자신을 확신 시키십시오.

어쩌면 Ajax 요청을 발생시키는 대신 서버에 명령을 보내고 서버 이벤트 스트림 (예 : 웹 소켓을 통해)을 수신하는 것으로 시작할 수 있습니다.

나는 Ajax 요청에 결코 익숙하지 않았다. 우리가 반응함에 따라 개발자는 기능적인 프로그래머 인 경향이 있습니다. 프론트 엔드 애플리케이션의 "진리 소스"인 로컬 데이터에 대해 추론하기는 어렵다고 생각하지만 실제 진실 소스는 실제로 서버 데이터베이스에 있으며 "로컬"진실 소스는 이미 구식 일 수 있습니다. 당신이 그것을받을 때, 당신이 절름발이 새로 고침 버튼을 누르지 않으면 진정한 진실 가치의 근원으로 수렴하지 않을 것입니다 ...이 엔지니어링입니까?

그러나 몇 가지 명백한 이유로 이러한 것을 디자인하는 것은 여전히 ​​어렵습니다.

  • 모바일 / 브라우저 클라이언트는 리소스가 제한되어 있으며 모든 데이터를 로컬에 저장할 필요는 없습니다.
  • 클라이언트는 분산 시스템의 모든 데이터를 볼 수 없으므로 보안상의 이유로 수신되는 이벤트를 필터링해야합니다.

3
Q 약속을 조치와 함께 사용하는 예를 제공 할 수 있습니까?
매트 폭스 던컨

@MattFoxxDun은 "이벤트 로그"를 직렬화 할 수없고 실행중인 조치에 대해 상점을 비동기식으로 업데이트하는 것이 좋은 아이디어인지 확실하지 않으므로 약간의 단점이 있습니다. 상용구를 줄입니다. Fluxxor를 사용하면 다음과 같은 작업을 수행 할 수 있습니다.this.dispatch("LOAD_DATA", {dataPromise: yourPromiseHere});
Sebastien Lorber

AJAX 인수에 대해 완전히 동의하지 않습니다. 실제로 읽는 것은 매우 성가신 일이었습니다. 당신은 당신의 말을 읽었습니까? 상점, 게임, 돈을 버는 앱, 모두 API 및 AJAX 서버 호출이 필요합니다. "서버리스"또는 그 성격을 원한다면 Firebase를 살펴보십시오. 그러나 AJAX는 적어도 아무도 동의하지 않기를 바랍니다. 당신의 논리
TheBlackBenzKid

@TheBlackBenzKid 나는 Ajax가 올해 완전히 사라질 것이라고 말하지는 않지만 (현재 스타트 업의 CTO 인 Ajax 요청 위에 웹 사이트를 계속 구축하고 있는지 확인), 나는 그것이 사라질 가능성이 있다고 말하고있다. 스트리밍이 필요하고 폴링이 필요하지 않은 최종 일관성을 처리하기에 충분한 프로토콜은 아니며, 최종 일관성은 앱을 오프라인에서 안정적으로 작동하도록 허용하는 것입니다 (예 : 로컬 스토리지를 사용하여 무언가를 해킹 할 수는 있지만 오프라인 용량이 제한적이거나 앱이 매우 간단합니다). 문제는 캐싱이 아니며 캐시를 무효화하는 것입니다.
Sebastien Lorber

@TheBlackBenzKid Firebase, Meteor 등의 모델은 충분하지 않습니다. 이러한 시스템이 동시 쓰기를 처리하는 방법을 알고 있습니까? 인과 적 합병 / 병합 전략 대신 최후 기록-승리? 둘 다 신뢰할 수없는 연결에서 작업 할 때 앱에서 동료의 작업을 재정의 할 수 있습니까? 또한 이러한 시스템은 로컬 및 서버 모델링을 많이 결합하는 경향이 있습니다. Firebase 사용자가 만족 스럽다고 선언하면서 상당히 복잡한 잘 알려진 공동 작업 앱을 알고 있습니까? 난하지 않습니다
Sebastien Lorber

20

활동 작성자 또는 상점에서 데이터를 호출 할 수 있습니다. 중요한 것은 응답을 직접 처리하지 않고 오류 / 성공 콜백에서 조치를 작성하는 것입니다. 상점에서 직접 응답을 처리하면 취성 설계가 더 복잡해집니다.


9
더 자세히 설명해 주시겠습니까? 서버에서 초기 데이터를로드해야한다고 가정 해보십시오. 컨트롤러보기에서 INIT 작업을 시작하고 Store는이 작업을 반영하여 비동기 초기화를 시작합니다. 이제 Store가 데이터를 가져올 때 단순히 변경 사항을 내보냈지만 동작을 시작하지는 않을 것이라는 생각에 착안했습니다. 따라서 초기화 후 변경 사항을 내 보내면 뷰에서 저장소에서 데이터를 가져올 수 있음을 알 수 있습니다. 성공적으로로드 할 때 변경 사항을 내 보내지 않고 다른 작업을 시작 해야하는 이유는 무엇입니까 ?! 감사합니다
Jim-Y

Fisherwebdev는 데이터를 호출하는 상점에 대해 Flux 패러다임을 깨뜨리지 않습니다. 데이터를 호출 할 것으로 생각할 수있는 유일한 두 가지 방법은 다음을 사용하는 것입니다. 뷰, 액션을 사용하여 데이터로드
Yotam

4
데이터를 호출하는 것은 데이터를받는 것과 다릅니다. @ Jim-Y : 상점의 데이터가 실제로 변경된 후에 만 ​​변경을 수행해야합니다. Yotam : 아니요. 스토어에서 데이터를 호출해도 패러다임이 깨지지 않습니다. 데이터는 조치를 통해서만 수신되어야하므로 응용 프로그램에 들어가는 새 데이터로 모든 상점에 알릴 수 있습니다. 따라서 상점에서 데이터를 호출 할 수 있지만 응답이 다시 발생하면 직접 처리하는 대신 새 조치를 작성해야합니다. 따라서 새로운 기능 개발에 유연하고 탄력적으로 애플리케이션을 유지할 수 있습니다.
fisherwebdev

2

Fluxxor ajax example 에서 Binary Muse의 예제를 사용하고 있습니다. 다음은 동일한 접근법을 사용하는 매우 간단한 예입니다.

간단한 제품 저장소에 일부 제품 작업제품 저장소의 변경 사항에 모두 응답하는 하위 구성 요소가있는 컨트롤러 뷰 구성 요소가 있습니다 . 예를 들어 product-slider , product-listproduct-search components.

가짜 제품 클라이언트

다음은 실제 엔드 포인트 반품 제품 호출을 대체 할 수있는 가짜 클라이언트입니다.

var ProductClient = {

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

module.exports = ProductClient;

제품 판매점

여기에 제품 저장소가 있습니다. 분명히 이것은 매우 작은 저장소입니다.

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;

이제 AJAX 요청 및 성공시 LOAD_PRODUCTS_SUCCESS 조치가 상점으로 제품을 리턴하는 제품 조치를 실행합니다.

제품 조치

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;

따라서이 this.getFlux().actions.productActions.loadProducts()상점을 청취하는 모든 컴포넌트에서 호출 하면 제품이로드됩니다.

addProduct(id) removeProduct(id)같은 패턴을 따르는 등의 사용자 상호 작용에 반응하는 다른 작업을 상상할 수 있습니다.

구현하기가 약간 까다로 웠지만 매장이 100 % 동기를 유지하는 데 도움이 되었기 때문에이 예제가 도움이 되길 바랍니다.


2

관련 질문에 대답했습니다 : 중첩 된 API 호출을 플럭스로 처리하는 방법

행동은 변화를 일으키는 것으로 생각되지 않습니다. 그들은 외부 세계의 변화를 응용 프로그램에 알리는 신문과 같아야하며, 그런 다음 응용 프로그램이 그 뉴스에 응답합니다. 상점 자체가 변경됩니다. 행동은 그들에게 알려줍니다.

Flux 제작자 Bill Fisher https://stackoverflow.com/a/26581808/4258088

기본적으로해야 할 일은 작업을 통해 필요한 데이터를 명시하는 것입니다. 상점이 조치에 대한 정보를 받으면 일부 데이터를 가져와야하는지 여부를 결정해야합니다.

상점은 필요한 모든 데이터를 축적 / 페치해야합니다. 그러나 상점이 데이터를 요청하고 응답을 얻은 후에는 상점이 응답을 직접 처리 / 저장하는 것과 반대로 페치 된 데이터로 조치 자체를 트리거해야합니다.

상점은 다음과 같이 보일 수 있습니다.

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

0

여기에 내가 취하는 것이 있습니다 : http://www.thedreaming.org/2015/03/14/react-ajax/

희망이 도움이됩니다. :)


8
지침에 따라 공감하십시오. 외부 사이트에 답을 올리면이 사이트의 유용성이 떨어지고 응답 품질이 낮아져 사이트의 유용성이 떨어집니다. 외부 URL도 시간이 지날 수 있습니다. downvote는 기사의 유용성에 대해 아무 말도하지 않습니다.
oligofren

2
좋은 글이지만 각 접근법의 장단점을 간략하게 추가하면 투표 할 수 있습니다. 따라서 답변의 요지를 얻기 위해 링크를 클릭하지 않아도됩니다.
코리 하우스
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.