フラックスアーキテクチャで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];
}
}
ストアでの読み取りには非同期関数が関係しますが、非同期ハンドラーではストアが自身を更新せず、代わりにアクションを起動し、応答が到着したときにonlyアクションを起動するという重要な注意事項があります。このアクションのハンドラーは、実際の状態変更を行うことになります。
たとえば、コンポーネントは次のことを行います。
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プロミスを置くのに非常に便利であることがわかりました。
AjaxはEVIL
Ajaxは、推論するのが非常に難しいため、近い将来、ますます使用されなくなると思います。正しい方法?分散システムの一部としてデバイスを考慮すると、このアイデアに最初に出会った場所はわかりません(多分、この中に Chris Grangerのインスピレーションビデオ )。
考えてみてください。スケーラビリティのために、ストレージエンジンとして結果整合性を備えた分散システムを使用します( CAP定理 に勝てず、多くの場合は利用可能にしたいため)。これらのシステムは、お互いのポーリングを通じて同期するのではなく(おそらくコンセンサス操作の場合を除く?)、代わりにCRDTやイベントログなどの構造を使用して、分散システムのすべてのメンバーを最終的に一貫性のあるものにします(十分な時間がある場合、メンバーは同じデータに収束します) 。
次に、モバイルデバイスまたはブラウザとは何かを考えます。これは、ネットワーク遅延とネットワーク分割の影響を受ける可能性のある分散システムの単なるメンバーです。(つまり、地下鉄でスマートフォンを使用しています)
ネットワークパーティションとネットワークスピードトレラントデータベースを構築できる場合(つまり、分離ノードへの書き込み操作を実行できることを意味します)、これらの概念に触発されたフロントエンドソフトウェア(モバイルまたはデスクトップ)を構築できます。アプリのないボックスの機能は利用できません。
フロントエンドアプリケーションをアーキテクチャ化するためにデータベースがどのように機能しているかについて、私たちは本当に刺激を受けるべきだと思います。注目すべきことの1つは、これらのアプリがPOSTおよびPUTおよびGET ajaxリクエストを実行して相互にデータを送信するのではなく、イベントログとCRDTを使用して結果整合性を確保することです。
それでは、なぜフロントエンドでそれをしないのですか?バックエンドはすでにその方向に動いており、Kafkaのようなツールが大企業に大々的に採用されていることに注意してください。これは何らかの形でイベントソーシング/ CQRS/DDDにも関連しています。
Kafka著者からのこれらの素晴らしい記事をチェックして、自分を納得させてください:
Ajaxリクエストを発行する代わりに、サーバーにコマンドを送信し、サーバーイベントのストリームを(例としてwebsocketを介して)受信することから始められるかもしれません。
Ajaxのリクエストに非常に満足したことはありません。 React開発者は機能的なプログラマーである傾向があります。フロントエンドアプリケーションの「真実のソース」となるローカルデータについて推論するのは難しいと思いますが、実際の真実のソースは実際にはサーバーデータベース上にあり、「ローカル」の真実のソースはすでに古くなっている可能性がありますあなたがそれを受け取ったとき、あなたがいくつかの不完全な更新ボタンを押さない限り、真理値の本当のソースに収束することはありません...このエンジニアリングですか?
しかし、いくつかの明らかな理由でそのようなものを設計するのはまだ少し難しいです:
アクション作成者またはストアのいずれかでデータを呼び出すことができます。重要なことは、応答を直接処理するのではなく、エラー/成功コールバックでアクションを作成することです。店舗で直接応答を処理すると、設計がより脆弱になります。
Fluxxor ajax example のBinary Museの例を使用しています。同じアプローチを使用した非常に簡単な例を次に示します。
私は簡単です 製品店 一部 製品アクション そしてその コントローラービュー すべてのサブコンポーネントを含むコンポーネントは、 製品店。例えば 製品スライダー、 製品リスト そして 製品検索 コンポーネント。
偽製品クライアント
製品を返す実際のエンドポイントを呼び出す代わりに使用できる偽のクライアントを次に示します。
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%同期状態に保つのに役立ったので、この例が少し役立つことを願っています。
ここで関連する質問に答えました: フラックスでネストされた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
}
}
これについての私の見解は次のとおりです。 http://www.thedreaming.org/2015/03/14/react-ajax/
お役に立てば幸いです。 :)