今日学习目录
一、 基本入门
二、 为了让代码便于测试
三、call、apply、cps
四、dispatch、put
一、 基本入门
创建一个 sagas.js 的文件,然后添加以下代码片段:
// sagas.js
export function* helloSaga() {
console.log('Hello Sagas!');
}
为了运行我们的 Saga,我们需要:
- 创建一个 Saga middleware 和要运行的 Sagas(目前我们只有一个 helloSaga)
- 将这个 Saga middleware 连接至 Redux store.
// main.js ...
import { createStore, applyMiddleware } from 'redux'
import createSagaMiddleware from 'redux-saga'
//...
import { helloSaga } from './sagas'
const sagaMiddle = createSagaMidlleware(helloSaga)
const store = createStore(
reducer,
applyMiddleware(sagaMiddle )
)
sagaMiddle.run(helloSaga)
// rest unchanged
运行 helloSaga 之前,我们必须使用 applyMiddleware 将 middleware 连接至 Store。然后使用 sagaMiddleware.run(helloSaga) 运行 Saga。
二、 为了让代码便于测试
import { call } 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')"
)
这些 声明式调用(declarative calls) 的优势是,我们可以通过简单地遍历 Generator 并在 yield 后的成功的值上面做一个 deepEqual 测试, 就能测试 Saga 中所有的逻辑。这是一个真正的好处,因为复杂的异步操作都不再是黑盒,你可以详细地测试操作逻辑,不管它有多么复杂。
三、call、apply、cps
// Effect -> 调用 Api.fetch 函数并传递 `./products` 作为参数
{
CALL: {
fn: Api.fetch,
args: ['./products']
}
}
call 同样支持调用对象方法,你可以使用以下形式,为调用的函数提供一个 this 上下文:
yield call([obj, obj.method], arg1, arg2, ...)
// 如同 obj.method(arg1, arg2 ...)
apply 提供了另外一种调用的方式:
yield apply(obj, obj.method, [arg1, arg2, ...])
call 和 apply 非常适合返回 Promise 结果的函数。另外一个函数 cps 可以用来处理 Node 风格的函数 (例如,fn(...args, callback) 中的 callback 是 (error, result) => () 这样的形式,cps 表示的是延续传递风格(Continuation Passing Style))。
import { cps } from 'redux-saga'
const content = yield cps(readFile, '/path/to/file')
当然你也可以像测试 call 一样测试它:
import { cps } from 'redux-saga/effects'
const iterator = fetchSaga()
assert.deepEqual(iterator.next().value, cps(readFile, '/path/to/file') )
四、dispatch、put
//... 传统dispatch,不便于测试
function* fetchProducts(dispatch)
const products = yield call(Api.fetch, '/products')
dispatch({ type: 'PRODUCTS_RECEIVED', products })
}
redux-saga 为此提供了另外一个函数 put,这个函数用于创建 dispatch Effect。
import { call, put } from 'redux-saga/effects'
//...
function* fetchProducts() {
const products = yield call(Api.fetch, '/products')
// 创建并 yield 一个 dispatch Effect
yield put({ type: 'PRODUCTS_RECEIVED', products })
}
现在,我们可以像上一节那样轻易地测试 Generator:
import { call, put } from 'redux-saga/effects'
import Api from '...'
const iterator = fetchProducts()
// 期望一个 call 指令
assert.deepEqual(
iterator.next().value,
call(Api.fetch, '/products'),
"fetchProducts should yield an Effect call(Api.fetch, './products')"
)
// 创建一个假的响应对象
const products = {}
// 期望一个 dispatch 指令
assert.deepEqual(
iterator.next(products).value,
put({ type: 'PRODUCTS_RECEIVED', products }),
"fetchProducts should yield an Effect put({ type: 'PRODUCTS_RECEIVED', products })"
)