发起 action 到 store

在前面的例子上更进一步,假设每次保存之后,我们想发起一些 action 通知 Store 数据获取成功了(目前我们先忽略失败的情况)。

我们可以找出一些方法来传递 Store 的 dispatch 函数到 Generator。然后 Generator 可以在接收到获取的响应之后调用它。

  1. //...
  2. function* fetchProducts(dispatch)
  3. const products = yield call(Api.fetch, '/products')
  4. dispatch({ type: 'PRODUCTS_RECEIVED', products })
  5. }

然而,该解决方案与我们在上一节中看到的从 Generator 内部直接调用函数,有着相同的缺点。如果我们想要测试 fetchProducts 接收到 AJAX 响应之后执行 dispatch, 我们还需要模拟 dispatch 函数。

相反,我们需要同样的声明式的解决方案。只需创建一个对象来指示 middleware 我们需要发起一些 action,然后让 middleware 执行真实的 dispatch。 这种方式我们就可以同样的方式测试 Generator 的 dispatch:只需检查 yield 后的 Effect,并确保它包含正确的指令。

redux-saga 为此提供了另外一个函数 put,这个函数用于创建 dispatch Effect。

  1. import { call, put } from 'redux-saga/effects'
  2. //...
  3. function* fetchProducts() {
  4. const products = yield call(Api.fetch, '/products')
  5. // 创建并 yield 一个 dispatch Effect
  6. yield put({ type: 'PRODUCTS_RECEIVED', products })
  7. }

现在,我们可以像上一节那样轻易地测试 Generator:

  1. import { call, put } from 'redux-saga/effects'
  2. import Api from '...'
  3. const iterator = fetchProducts()
  4. // 期望一个 call 指令
  5. assert.deepEqual(
  6. iterator.next().value,
  7. call(Api.fetch, '/products'),
  8. "fetchProducts should yield an Effect call(Api.fetch, './products')"
  9. )
  10. // 创建一个假的响应对象
  11. const products = {}
  12. // 期望一个 dispatch 指令
  13. assert.deepEqual(
  14. iterator.next(products).value,
  15. put({ type: 'PRODUCTS_RECEIVED', products }),
  16. "fetchProducts should yield an Effect put({ type: 'PRODUCTS_RECEIVED', products })"
  17. )

现在我们通过 Generator 的 next 方法来将假的响应对象传递到 Generator。在 middleware 环境之外, 我们可完全控制 Generator,通过简单地模拟结果并还原 Generator,我们可以模拟一个真实的环境。 相比于去模拟函数和窥探调用(spying calls),模拟数据要简单的多。