Fluxアプリのどこでajaxリクエストを行う必要がありますか?


194

私はフラックスアーキテクチャを備えたreact.jsアプリケーションを作成しており、サーバーからのデータ要求がいつどこで行われるべきかを把握しようとしています。この例はありますか?(TODOアプリではありません!)

回答:


127

私は、アクションの作成者に非同期の書き込み操作を、ストアに非同期の読み取り操作を置くことを強く支持しています。目標は、ストア状態変更コードを完全に同期したアクションハンドラーに保持することです。これにより、推論が簡単になり、単体テストも簡単になります。同じエンドポイントへの複数の同時要求(たとえば、二重読み取り)を防ぐために、実際の要求処理を、複数の要求を防ぐためのpromiseを使用する別のモジュールに移動します。例えば:

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

アクションペイロード内にプロミスを入れようとしましたか?複数のアクションをディスパッチするよりも対処が簡単だと思います
Sebastien Lorber 2014年

@SebastienLorber私にとってフラックスの大きな魅力は、すべての状態更新を同期コードパスに維持することであり、明示的にアクションディスパッチの結果としてのみなので、店内での非同期を回避します。
ミシェルティリー2014年

1
@Federico「最良の」ソリューションが何であるかは、まだはっきりしていません。私は、未処理の非同期リクエストの数をカウントすることと組み合わせてデータをロードするためのこの戦略を実験してきました。残念ながらflux、構築後にストアに注入されるため、initializeメソッドでアクションを取得する優れた方法はありません。Yahooの同形のフラックスライブラリから良いアイデアが見つかるかもしれません。これはFluxxor v2がより適切にサポートするものです。これについてチャットしたい場合は、遠慮なく私にメールしてください。
ミシェルティリー

1
data: resultである必要がdata : dataありますか?ありませんresult。おそらく、データパラメータの名前をペイロードなどに変更することをお勧めします。
オリゴフレン2015年

2
私はこの古いスレッドが非常に役立つことがわかりました-特に、ビル・フィッシャーとジン・チェンのコメント。これは、@ BinaryMuseが提案していることと非常に似ていますが、アクションの作成者でディスパッチが発生するという小さな違いがあります。
フィリップウェイ、2015年

37

Fluxxorには APIとの非同期通信のがあります。

このブログ投稿はそれについて話し、Reactのブログで取り上げられました。


フロントエンドソフトウェアとバックエンドとの同期はまだ苦痛であるため、これはまだ明確に回答されていない非常に重要で難しい質問だと思います。

JSXコンポーネントでAPIリクエストを行う必要がありますか?店舗?別の場所?

ストアでリクエストを実行すると、2つのストアが特定のアクションに対して同じデータを必要とする場合、2つの同様の要求が発行されます(ストア間の依存関係を導入しない限り、私は本当に気に入らない

私の場合、次の理由により、Qの約束をアクションのペイロードとして配置するのに非常に便利です。

  • アクションをシリアル化する必要はありません(イベントログを保持していません。イベントソースのイベント再生機能は必要ありません)。
  • さまざまなアクション/イベント(リクエストの発生/リクエストの完了/リクエストの失敗)を行う必要がなくなり、同時リクエストを発生できるときに相関IDを使用してそれらを一致させる必要があります。
  • これにより、ストア間に依存関係を導入することなく、複数のストアが同じ要求の完了をリッスンできるようになります(ただし、キャッシングレイヤーを導入した方がよい場合もあります)。

Ajaxは悪

Ajaxは、推論するのが非常に難しいため、近い将来使用されることが少なくなると思います。正しい方法?デバイスを分散システムの一部と見なしているので、このアイデアに最初に出会った場所はわかりません(おそらく、この刺激的なChris Grangerビデオで)。

それについて考えてください。スケーラビリティのために、ストレージエンジンとして結果整合性を備えた分散システムを使用します(CAPの定理に勝ることはできず、多くの場合、利用可能にしたいためです)。これらのシステムは、相互にポーリングすることで同期しません(コンセンサス操作を除く)?ではなく、CRDTやイベントログなどの構造を使用して、分散システムのすべてのメンバーを最終的に整合させます(十分な時間が与えられれば、メンバーは同じデータに収束します) 。

次に、モバイルデバイスまたはブラウザとは何かについて考えます。これは、ネットワークレイテンシとネットワーク分割に悩まされる可能性がある分散システムのメンバーにすぎません。(つまり、地下鉄でスマートフォンを使用しています)

ネットワークパーティションとネットワークスピードトレラントデータベースを構築できる場合(つまり、孤立したノードへの書き込み操作を引き続き実行できることを意味します)、これらの概念にインスパイアされたフロントエンドソフトウェア(モバイルまたはデスクトップ)を構築できます。アプリのないボックスの機能は利用できません。

私たちは、データベースがフロントエンドアプリケーションを構築するためにどのように機能しているかに本当に刺激を与える必要があると思います。注目すべき点の1つは、これらのアプリがPOSTおよびPUTとGET ajaxリクエストを実行して互いにデータを送信するのではなく、イベントログとCRDTを使用して結果の一貫性を確保することです。

それで、なぜフロントエンドでそれをしないのですか?バックエンドはすでにその方向に進んでおり、Kafkaのようなツールは大規模なプレーヤーによって大規模に採用されています。これは何らかの形でイベントソーシング/ CQRS / DDDにも関連しています。

Kafkaの作者によるこれらの素晴らしい記事をチェックして、納得してください。

Ajaxリクエストを実行する代わりに、コマンドをサーバーに送信し、サーバーイベントのストリームを(たとえば、Webソケットを介して)受信することから始めることができます。

私はAjaxのリクエストにとても満足していません。Reactの開発者は関数型プログラマーである傾向があります。フロントエンドアプリケーションの「真の情報源」となるはずのローカルデータについて推論するのは難しいと思いますが、実際の真の情報源は実際にはサーバーデータベースにあり、「ローカル」の真実の情報源はすでに古くなっている可能性がありますあなたがそれを受け取ったとき、あなたがいくつかの不完全な更新ボタンを押さない限り、真の真の価値の源に決して収束しません...これはエンジニアリングですか?

しかし、いくつかの明らかな理由のために、そのようなものを設計することはまだ少し難しいです:

  • モバイル/ブラウザークライアントのリソースは限られているため、必ずしもすべてのデータをローカルに保存できるわけではありません(そのため、ajaxリクエストの重いコンテンツでのポーリングが必要になる場合があります)。
  • クライアントは分散システムのすべてのデータを表示するべきではないため、セキュリティ上の理由で受信したイベントを何らかの方法でフィルタリングする必要があります

3
アクションでQプロミスを使用する例を提供できますか?
マットフォックスダンカン

@MattFoxxDunは、「イベントログ」をシリアル化不可能にし、アクションが発生するとストアを非同期に更新するため、これが良いアイデアであるとは確信できません。したがって、いくつかの欠点があります。ボイラープレートを減らします。Fluxxorを使用すると、おそらく次のようなことができますthis.dispatch("LOAD_DATA", {dataPromise: yourPromiseHere});
Sebastien Lorber

AJAXの議論について完全に反対します。実際、読むのはとても面倒でした。あなたはあなたの発言を読みましたか?お金を稼ぐストア、ゲーム、アプリを考えてください。すべてAPIとAJAXサーバー呼び出しが必要です。「サーバーレス」またはそのような性質が必要な場合はFirebaseを見てください。ただし、AJAXは、少なくとも他の誰も同意しないことを願っています。あなたの論理
TheBlackBenzKid

@TheBlackBenzKid Ajaxが1年で完全になくなると言っているわけではありません(そして、スタートアップのCTOとして現在もajaxリクエストに基づいてWebサイトを構築していることを確認してください)が、消えそうです。ストリーミングではなくポーリングを必要とする結果整合性を処理するのに十分なプロトコルではありません。結果整合性は、アプリを確実にオフラインで動作させるために許可されます(はい、ローカルストレージで何かをハックできますが、オフラインキャパシティは制限されます。アプリは非常にシンプルです)。問題はキャッシュではなく、キャッシュを無効にすることです。
Sebastien Lorber 2016年

@TheBlackBenzKid Firebase、Meteorなどの背後にあるモデルは十分ではありません。これらのシステムが同時書き込みをどのように処理するか知っていますか?因果一貫性/マージ戦略の代わりに最後に書き込み勝ち?両方が信頼できない接続で作業しているときに、アプリで同僚の作業をオーバーライドする余裕がありますか?また、これらのシステムは、ローカルモデルとサーバーモデルの多くを組み合わせる傾向があることにも注意してください。非常に複雑で、完全にオフラインで機能し、満足のいくFirebaseユーザーであることを宣言している有名なコラボレーションアプリを知っていますか?私はしません
Sebastien Lorber

20

アクションクリエーターまたはストアのいずれかでデータを呼び出すことができます。重要なことは、応答を直接処理するのではなく、エラー/成功のコールバックでアクションを作成することです。ストアで直接応答を処理すると、設計がよりもろくなります。


9
これについて詳しく説明していただけますか?サーバーから初期データをロードする必要があるとしましょう。コントローラービューで、アクションINITを開始し、ストアは、このアクションを反映して非同期の初期化を開始します。さて、私は、ストアがデータをフェッチしたときに、変更を発行するだけで、アクションを開始しないという考えに取り組みます。したがって、初期化後に変更を発行すると、ストアからデータを取得できることがビューに通知されます。ロードが成功したときに変更を発行せに、別のアクションを開始する必要があるのはなぜですか?ありがとう
Jim-Y

データを呼び出すストアについて、Fisherwebdevは、そうすることで、Fluxパラダイムを破らないでください。データを呼び出すために考えられる適切な2つの方法は、次のものを使用することです。1。アクションを使用してブートストラップクラスを使用し、データをロードする2 。ビュー、再びアクションを使用してデータを読み込む
Yotam

4
データの呼び出しは、データの受信とは異なります。@ Jim-Y:ストア内のデータが実際に変更された場合にのみ、変更を発行する必要があります。Yotam:いいえ、ストアでデータを呼び出すことはパラダイムを壊しません。データはアクションを通じてのみ受信する必要があります。これにより、すべてのストアは、アプリケーションに入る新しいデータによって通知を受けることができます。したがって、ストアでデータを呼び出すことができますが、応答が返されたときに、直接処理するのではなく、新しいアクションを作成する必要があります。これにより、アプリケーションは柔軟になり、新機能の開発に対して回復力があります。
fisherwebdev 2015年

2

私はFluxxor ajaxの例からBinary Museの例を使用しています。これは、同じアプローチを使用した私の非常に単純な例です。

単純な製品ストアにいくつかの製品アクションと、すべてが製品ストアに加えられた変更に応答するサブコンポーネントを持つコントローラービューコンポーネントがあります。たとえば、product-sliderproduct-listproduct-searchコンポーネントです。

偽の製品クライアント

これは、製品を返す実際のエンドポイントを呼び出す代わりに使用できる偽のクライアントです。

var ProductClient = {

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

module.exports = ProductClient;

製品ストア

こちらがProduct 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;

次に、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もおそらく壊れます。反対票は、記事の有用性については何も述べていません。ところで、これは非常に優れています:)
オリゴフレン2015年

2
良い投稿ですが、各アプローチの長所/短所の短い要約を追加すると、賛成票が得られます。SOでは、リンクをクリックして回答の要点を取得する必要はありません。
Cory House、
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.