非同期アクション
これまでのチュートリアルでは、todoアプリケーションを構築しました。これは完全に同期的なもので、アクションがディスパッチされるたびにステートが即座に更新されます。 これから、異なる非同期アプリケーションを構築します。これはReddit APIを使用して、選択されたカテゴリのヘッドラインを表示します。
アクション
非同期APIを呼び出す際には、2つの極めて重要な瞬間があります。呼び出しを開始する瞬間と、応答を受け取る瞬間です。
これら2つの瞬間は通常、アプリケーションのステートに変更を加える必要があります。そのためには、リデューサーによって同期的に処理される通常のアクションをディスパッチする必要があります。通常、任意のAPIリクエストに対して、少なくとも3つの異なるアクションをディスパッチする必要があります。
- リデューサーにリクエストの開始を通知するアクション
リデューサーは、ステート内の
isFetching
フラグを切り替えることでこのアクションを処理するかもしれません。これはUIに待機インジケーターを表示する時期であることを伝えます。 - リデューサーにリクエストが正常に完了したことを通知するアクション
リデューサーは、新しいデータを制御下のステートにマージし、
isFetching
をリセットすることでこのアクションを処理するかもしれません。UIは待機インジケーターを非表示にし、取得したデータを表示します。 - リデューサーにリクエストが失敗したことを通知するアクション
リデューサーは、
isFetching
をリセットすることでこのアクションを処理するかもしれません。さらに、一部のリデューサーはこのエラー情報を保存して、UIに表示できるようにしたいと考えるかもしれません。
アクションに専用の status
フィールドを追加する必要があるかもしれません。
{type: 'FETCH_POSTS'}
{type: 'FETCH_POSTS', status: 'error', error: 'Oops'}
{typs: 'FETCH_POSTS', status: 'success', response: { ... }}
あるいは、それらに対して個別の型を定義します。
{ type: 'FETCH_POSTS_REQUEST' }
{ type: 'FETCH_POSTS_FAILURE', error: 'Oops' }
{ type: 'FETCH_POSTS_SUCCESS', response: { ... } }
フラグを持つ単一のアクション型を選択するか、複数のアクション型を選択するかは、あなた次第です。複数の型を使用する方が間違いの余地は少なくなりますが、redux-actions のようなヘルパーライブラリを使用してアクションクリエーターとリデューサーを生成する場合は問題ありません。
非同期データフロー
ミドルウェアを使用しない場合、Reduxストアは同期的なデータフローのみを提供します。これは createStore()
を通じて得られるデフォルトの結果です。
applyMiddleware()
を使用して createStore()
を強化できます。これは必須ではありませんが、非同期アクションを便利な方法で記述できるようになります。
redux-thunk
や redux-promise
のような非同期ミドルウェアは、ストアの dispatch()
メソッドをラップし、アクション以外の内容(関数やPromiseなど)をディスパッチできるようにします。使用するミドルウェアは、ディスパッチするあらゆる内容を独自の方法で解析し、次のミドルウェアにアクションを渡し続けることができます。例えば、PromiseをサポートするミドルウェアはPromiseをインターセプトし、各Promiseに対して非同期的に開始/終了アクションのペアをディスパッチできます。
データフロー上の最後のミドルウェアがアクションをディスパッチするとき、それは通常のオブジェクトでなければなりません。ここから同期的なReduxデータフローが始まります。
ミドルウェア
ミドルウェアは、アクションのディスパッチとそれがリデューサーに到達する間のタイミングで、サードパーティの拡張ポイントを提供します。人々はミドルウェアを使って、ログの出力、クラッシュレポートの記録、非同期APIの呼び出し、ルーティングなどを行います。 ミドルウェアの強力な機能を示すいくつかの例があります。
ミドルウェアの理解
ミドルウェアは多くのことを実行できますが、それがどこから来たのかを理解することが非常に重要です。ロギングとクラッシュレポートの2つの例を使用して、ミドルウェアを使用する思考プロセスを示します。
問題: ロギング
試行#1:手動ロギング
最も原始的な解決策は、store.dispatch(action)
を呼び出すたびにアクションと次のステートを記録することです。コードは次のようになるでしょう。
let action = addTodo("Use Redux");
console.log("dispatching", action);
store.dispatch(action);
console.log("next state", store.getState());
これは本当にひどい方法です。
試行#2:ディスパッチのラッピング
ロギングを関数に抽出します。
function dispatchAndLog(store, action) {
console.log("dispatching", action);
store.dispatch(action);
console.log("next state", store.getState());
}
これで、呼び出すたびにこの関数を使用して store.dispatch()
を置き換えます。
dispatchAndLog(store, addTOdo("USe Redux"));
これで問題は解決できますが、エレガントではありません。
試行#3:ディスパッチのモンキーパッチ
ストア内で dispatch
関数を置き換えるだけではどうでしょうか? Reduxのストアはいくつかのメソッドを持つ単なるプレーンなオブジェクトなので、dispatch
を書き換えることができます。
let next = store.dispatch
store.dispath = fucntion dispatchAndLog (action){
console.log('dispatching',action)
let result = next(action)
console.log('next state',soter.getState())
return reslut
}
これで基本的に私たちの要求は満たされます。どこでアクションをディスパッチしても、それが記録されることが保証されます。ストアの内部メソッドをモンキーパッチで書き換えましたが、とりあえずこれで進めましょう。
この記事は 2016年5月22日 に公開され、2016年5月22日 に最終更新されました。3424 日が経過しており、内容が古くなっている可能性があります。