Redux-SagaのEffectについて
前回までのあらすじ
そもそもなぜ私はRedux-SagaのEffectが理解できないのか
- 概念がシンプルで何もとっかかりがないのでわかりにくい
- 何を伝えたいのかがわからなくピンとこない
プレーンなjavaScriptのオブジェクト
generator関数の中でyieldするのはプレーンなjavaScriptオブジェクト
らしい
つまり、1とか"hoge"とか{"count": 10}とかそう言ったものを私は想像してみた
そして
EffectもプレーンなjavaScriptオブジェクトと同じみたいだ
また、Effectはmiddlewareを理解する上での案内役になってくれる
つまりどんなEffectを使うかによってRedux-Sagaのmiddlewareとしての色も変わってくる
具体的には非同期関数を呼び出したり、actionをdispatchしてstoreを更新するなど、Effectができることは様々
import { takeEvery } from 'redux-saga/effects'
import Api from './path/to/api'
function* watchFetchProducts() {
yield takeEvery('PRODUCTS_REQUESTED', fetchProducts)
}
function* fetchProducts() {
const products = yield Api.fetch('/products')
console.log(products)
}
Redux-Sagaの実装で一番手軽に利用する方法はEffectを使わずに直接promiseを呼び出す
yield Api.fetch('/products')
Effectを使わないgenerator関数の呼び出しはこんな感じの図が書けるかもしれない
const iterator = fetchProducts()
assert.deepEqual(iterator.next().value, ??) // what do we expect ?
テストコードは上記の感じになる
上記のコードで伝えたいことは、プレーンなpromise呼び出しだとレスポンスデータでどんなものが帰ってくるかわからないということ
受け手は生のデータを見て、整合性を判定しなければならないため大変
Effectを使った場合
import { call } from 'redux-saga/effects'
function* fetchProducts() {
const products = yield call(Api.fetch, '/products')
// ...
}
yield call(Api.fetch, '/products')
このようにEffectの中のcall
機能を使うことでテストが容易になる
なぜならテスト実行者は
「Effectbのcall機能を使ってApiを呼び出した」middlewareの動作をテストし、中身となる生のデータについて検証しないようにできる
// expects a call instruction
assert.deepEqual(
iterator.next().value,
call(Api.fetch, '/products'),
"fetchProducts should yield an Effect call(Api.fetch, './products')"
)
declarative Effectsの真意に迫ってみる
declarative(宣言) Effectsが一応Redux-SagaにおけるEffect機能の正式名称っぽい
ということはこの「宣言」にもそれなりに意味はありそう
テストの方法で見てきたように Effectで何を使っているかが大切だとわかった
さらにdeclative callsの意味に寄り添って考えれば
Effectで「〇〇をします!」と言った(宣言)したことにフォーカスする意図ではないかと思う
Effectのまとめ
- EffectはプレーンなjavaScriptオブジェクト
- middleWareとしての役割の中核を担っている
- declarative (宣言)という機能がmiddleWareそのもの(と言ってもいいかもしれない)で
- 宣言をテストでチェックすることができる
Discussion