Redux-SagaのEffectについて

2022/07/21に公開約2,500字

前回までのあらすじ

そもそもなぜ私は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

ログインするとコメントできます