ReactをFluxで使う際、外部APIをキックしてデータを取得する非同期処理をどこに持たせるか。
ここの情報がドキュメントによって様々な為、悩んだ結果、以下のようにしました。
環境
React.js: 0.13.3
要望
- Storeに非同期処理を持たせたくない
(Store自身が非同期にデータを取得する処理を行う、というのをやめたい) -
API -> Action ではなく、 Action -> API にしたい
(非同期データの取得を待ち合わせ、Actionをコールしてデータを渡す、というのをやめたい)
実装
かなり簡略化してます。
app.jsx
import React from "react/addons"
import App from "./App";
// エンドポイントはまずコンポーネントの描画
React.render(<App id={<何らかの値>}/>, document.getElementById("app"));
App.jsx
// ~~ import処理 ~~
getState () {
return {
data: Store.getData()
};
}
export default class App extends React.component {
constructor () {
this.state = getState();
}
componentWillMount () {
// データ初期化処理をコール
Action.init(this.props.id);
}
// ~~ Storeへのlisten ~~
_onChange () {
this.setState(getState());
}
render () {
// データ初期化前はfalseを返してリターン
if (!this.state.data) {
return false;
}
// ~~ render処理 ~~
}
}
Action.jsx
// ~~ import処理 ~~
export default class Action {
// アクションのメソッドがAPIをコールする
init (id) {
API.get(id)
.done(data => {
// データの取得が終わったらStoreに通知
Dispatcher.dispatch({
type: Constants.INIT,
data: data
});
});
}
}
Store.jsx
// ~~ import処理 ~~
var _data = null;
var Store = assign({}, EventEmitter.prototype, {
getData () {
return _data;
},
// ~~ emit、listener ~~
});
Dispatcher.register(action => {
switch (action.type) {
case Constants.INIT:
_data = action.data;
break;
}
});
export default Store;
最初にコンポーネントをレンダリングし、コンポーネント内でデータの有無を判定してActionをコールしています。
(componentWillMount
はサーバサイドでは動作しないため、クライアントサイド実装に限られますが…)
もっと良いやり方がある気がしますが、一応これで要望を満たす実装はできました。