redux-saga
redux-sagaはReduxで副作用を扱うためのRedux Middleware
https://redux-saga.js.org/
React applicationにおける非同期処理は実質redux-thunkとredux-sagaの二択
redux-sagaは「タスク」という概念をReduxに持ち込む
タスクというのはプロセスのような独立した実行単位で、それぞれが別々に並行して動作する
redux-sagaはこのタスクの実行環境を提供します
それに加えて非同期処理をタスクとして記述するための道具立てである 「作用(Effects)」 と非同期処理を同期的に書き下す手段も提供してくれます
作用というのはタスクを記述するためのコマンド(命令、プリミティブ)のようなもので、例えば次のようなものがあります
select: Stateから必要なデータを取り出す
put: Actionをdispatchする
take: Actionを待つ、イベントの発生を待つ
call: Promiseの完了を待つ
fork: 別のタスクを開始する
join: 別のタスクの終了を待つ
Generator
redux-sagaのタスクをGenerator関数で書く理由は yield によって処理の流れを一時停止するため
この仕組みのおかげでシングルスレッドのJavaScriptで複数のタスクを立ち上げて、それぞれで特定のActionを待ったり、通信処理の結果待ちができる
redux-thunkと比較して良いこと
Action Creatorがシンプルになる
redux-thunkはAction Creatorが関数を投げる
必然的にAction Creatorに非同期処理のcodeや関連するlogicを詰め込むことになる
redux-sagaは非同期処理を記述する専用の仕組みであるタスクに書く
Action Creatorは本来の姿を取り戻して、Action objectを生成して返すだけになる
Redux Middlewareのどの位置にも配置できる
redux-thunkはmiddlewaresの一番外側に配置しないといけない
redux-thunkはdispatchされた関数を捕まえてひたすら実行する
https://github.com/reduxjs/redux-thunk/blob/237f6bb91a3d99e36ee756d8b315bdb4965e4a3a/src/index.js#L3-L5
code:js
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
普通のmiddlewareは関数を受け取れずにerrorになるので、まずredux-thunkが受け取って処理しないといけない
redux-loggerのようなmiddlewareでdebugしづらい
関数なので中身がどうなっているかは実行するかわからない
redux-sagaはplainなaction objectをdispatchする
順番を気にしない
loggingした時に中身がわかる、debugしやすい
API呼び出しのチェインがスムーズ
redux-thunkでは API.get('/path/a').then((...) => { API.get('/path/b').then((...) => {...}) }) のようにネストしてしまう
async/awaitでも書けるが、Action Creatorに処理を追記していくことになる
redux-sagaではもともとのAPI callを行うタスクに手を入れることなく書ける
そのタスクのなかでdispatchするactionを待つタスクを書けば良い
タスクは他のタスクで何をやっているかは気にしない
テストが書きやすい
redux-sagaではタスクのテストを書く
タスクはただのGenerator関数で、生成されたGeneratorがnext()で返すのはただのオブジェクト
つまりオブジェクトの値を比較するだけで済む
テストするべきはこのタスクが何をしようとしているかであって、その先で何をするかは知る必要がない
上述のAPI呼び出しチェインの場合では、API呼び出しが成功した後にもう一度別のAPIを呼び出すことをテストしないといけない
参考
https://qiita.com/kuy/items/716affc808ebb3e1e8ac
React applicationにおける非同期処理