トラブルシューティング
Sagaを追加するとアプリがフリーズする
ジェネレーター関数からエフェクトを yield
していることを確認してください。
次の例を考えてみましょう
import { take } from 'redux-saga/effects'
function* logActions() {
while (true) {
const action = take() // wrong
console.log(action)
}
}
これは、take()
がエフェクトの記述を作成するだけであるため、アプリケーションを無限ループに陥れます。ミドルウェアが実行するために yield
しない限り、while
ループは通常の while
ループのように動作し、アプリケーションがフリーズします。
yield
を追加すると、ジェネレーターが一時停止し、Redux Saga ミドルウェアに制御が戻り、エフェクトが実行されます。take()
の場合、Redux Saga はパターンに一致する次のアクションを待機し、その後にのみジェネレーターを再開します。
上記の例を修正するには、take()
によって返されるエフェクトを yield
します。
import { take } from 'redux-saga/effects'
function* logActions() {
while (true) {
const action = yield take() // correct
console.log(action)
}
}
Sagaがディスパッチされたアクションを欠落させている
Sagaが何らかのエフェクトでブロックされていないことを確認してください。Sagaがエフェクトの解決を待機している場合、エフェクトが解決されるまでディスパッチされたアクションを受け取ることができません。
たとえば、次の例を考えてみましょう
function* watchRequestActions() {
while (true) {
const { url, params } = yield take('REQUEST')
yield call(handleRequestAction, url, params) // The Saga will block here
}
}
function* handleRequestAction(url, params) {
const response = yield call(someRemoteApi, url, params)
yield put(someAction(response))
}
watchRequestActions
が yield call(handleRequestAction, url, params)
を実行すると、次の yield take
に進む前に、handleRequestAction
が終了して戻るまで待機します。たとえば、次のようなイベントシーケンスがあるとします。
UI watchRequestActions handleRequestAction
-----------------------------------------------------------------------------
.......................take('REQUEST').......................................
dispatch(REQUEST)......call(handleRequestAction).......call(someRemoteApi)... Wait server resp.
.............................................................................
.............................................................................
dispatch(REQUEST)............................................................ Action missed!!
.............................................................................
.............................................................................
.......................................................put(someAction).......
.......................take('REQUEST')....................................... saga is resumed
上記のように、Sagaがブロッキング呼び出しでブロックされている場合、その間にディスパッチされたすべてのアクションを見逃します。
Sagaのブロックを回避するには、call
の代わりに fork
を使用して、非ブロッキング呼び出しを使用できます。
function* watchRequestActions() {
while (true) {
const { url, params } = yield take('REQUEST')
yield fork(handleRequestAction, url, params) // The Saga will resume immediately
}
}
ルートSagaにバブリングするエラーのエラースタックが読みにくい
Saga内のタスクは本質的に非同期であるため、同期呼び出しのチェーンであるかのように「Sagaスタック」を表示するために、追加の作業を行う必要があります。そのため、redux-saga@v1
以降では、エラーがルートSagaにバブリングすると、ライブラリはその「Sagaスタック」を構築し、onError
コールバックの2番目の引数のプロパティ sagaStack: string
として渡します(ミドルウェアオプションも参照)。これにより、エラー追跡システムに送信したり、その他の追加作業を行うことができます。
結果として、コンソールに次のようなものが表示されます。
開発目的で、ファイル名と行番号を含むこれらの「Sagaスタック」が必要な場合は、babel-plugin を追加できます。これにより、拡張された情報を得ることができます。ドキュメントはこちらで入手できます。babel-pluginの使用例については、この例を確認してください。
babel-plugin-redux-saga
を追加すると、同じ出力は次のようになります。
注:テストでも同様に動作します。sagaをsagaMiddleware
経由で実行することを確認してください(またはランナー)。