エラーハンドリング
このセクションでは、前の例で失敗したケースをどのように処理するかを見ていきます。API 関数 Api.fetch
が、何らかの理由でリモートフェッチが失敗した場合に拒否される Promise を返すとしましょう。
PRODUCTS_REQUEST_FAILED
アクションをストアにディスパッチして、それらのエラーを Saga 内で処理します。
おなじみの try/catch
構文を使用して、Saga 内でエラーをキャッチできます。
import Api from './path/to/api'
import { call, put } from 'redux-saga/effects'
// ...
function* fetchProducts() {
try {
const products = yield call(Api.fetch, '/products')
yield put({ type: 'PRODUCTS_RECEIVED', products })
}
catch(error) {
yield put({ type: 'PRODUCTS_REQUEST_FAILED', error })
}
}
失敗のケースをテストするには、ジェネレーターの throw
メソッドを使用します。
import { call, put } from 'redux-saga/effects'
import Api from '...'
const iterator = fetchProducts()
// expects a call instruction
assert.deepEqual(
iterator.next().value,
call(Api.fetch, '/products'),
"fetchProducts should yield an Effect call(Api.fetch, './products')"
)
// create a fake error
const error = {}
// expects a dispatch instruction
assert.deepEqual(
iterator.throw(error).value,
put({ type: 'PRODUCTS_REQUEST_FAILED', error }),
"fetchProducts should yield an Effect put({ type: 'PRODUCTS_REQUEST_FAILED', error })"
)
この場合、 throw
メソッドに偽のエラーを渡しています。これにより、ジェネレーターは現在のフローを停止し、catch ブロックを実行します。
もちろん、API エラーを try
/catch
ブロック内で処理する必要はありません。API サービスに、エラーフラグが付けられた通常の値を返させることもできます。たとえば、プロミスの拒否をキャッチして、それらをエラーフィールドを持つオブジェクトにマップできます。
import Api from './path/to/api'
import { call, put } from 'redux-saga/effects'
function fetchProductsApi() {
return Api.fetch('/products')
.then(response => ({ response }))
.catch(error => ({ error }))
}
function* fetchProducts() {
const { response, error } = yield call(fetchProductsApi)
if (response)
yield put({ type: 'PRODUCTS_RECEIVED', products: response })
else
yield put({ type: 'PRODUCTS_REQUEST_FAILED', error })
}
onError フック
フォークしたタスク内のエラーは 親までバブルアップ し、キャッチされるかルート Saga に到達するまで続きます。エラーがルート Saga に伝播すると、Saga ツリー全体がすでに 終了 しています。この場合の推奨されるアプローチは、onError フック を使用して例外を報告し、ユーザーに問題を通知して、アプリを正常に終了することです。
onError
フックをグローバルエラーハンドラとして使用できないのはなぜですか? 通常、例外はコンテキスト依存するため、万能の解決策はありません。onError
フックを予期しないエラー処理のための最終的な手段と見なしてください。
エラーの伝播を望まない場合は、セーフラッパーの使用を検討してください。例はこちら