概念
redux-saga是redux的中间件,中间件的作用是为redux提供额外的功能,用来处理异步产生的副作用
sages使用generator来yield Effects
effects API 如 fork,call,take,put,cancel 等来创建 Effect。
redux-saga
//拿到createSagaMiddleware中间件
import createSagaMiddleware from "redux-saga";
//创建中间件
const sagaMiddleware = createSagaMiddleware()
//将中间件注入store
const store = createStore(reducer, applyMiddleware(sagaMiddleware))
//启动中间间,将rootSaga配置放入中间件
sagaMiddleware.run(rootSaga)
saga配置
//redux-saga/effects拿到处理异步dispatch的方法
import { put, takeEvery, delay } from "redux-saga/effects";
//模拟异步请求,action的时候执行
export function* incrementAsync() {
//延时
yield delay(1000);
//派发action
yield put({ type: "INCREMENT" });
}
//中间件启动的时候执行
export default function* rootSaga() {
//并行执行异步请求
yield takeEvery("INCREMENT_ASYNC", incrementAsync);
}
//模拟执行过程
<Counter
value={store.getState()}
onIncrement={() => action('INCREMENT')}
onDecrement={() => action('DECREMENT')}
onIncrementIfOdd={() => action('INCREMENT_IF_ODD')}
onIncrementAsync={() => action('INCREMENT_ASYNC')}
/>,
//takeLatest
export function* incrementAsync() {
yield delay(500);
yield put({ type: "INCREMENT" });//只派发最新的一次action
}
export default function* rootSaga() {
//只执行最新的action
yield takeLatest("INCREMENT_ASYNC", incrementAsync);
}
redux-saga分类
worker saga做调用api,的异步操作
watcher saga监听被dispatch的actions,收到actions消息后通知worker执行任务
root saga 立即启动saga的唯一入口
Effect概念
一个 Saga 所做的实际上是组合那些所有的 Effect,共同实现所需的控制流。 最简单的例子是直接把 yield 一个接一个地放置来对序列化 yield Effect。你也可以使用熟悉的控制流操作符(if, while, for) 来实现更复杂的控制流。
错误处理,try & catch
//用try,catch来捕获异步执行中的错误
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 })
}
}
dispatch action,put
//用put来派发异步请求结果action
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 })
}
声明式Effects,call
//用call来代替普通的异步请求
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')"
)
Saga Helpers,takeLates * takeEvery
import { takeLatest } from 'redux-saga'
function* watchFetchData() {
yield* takeLatest('FETCH_REQUESTED', fetchData)
}
import { takeEvery } from 'redux-saga/effects'
// FETCH_USERS
function* fetchUsers(action) { ... }
// CREATE_USER
function* createUser(action) { ... }
// 同时使用它们
export default function* rootSaga() {
yield takeEvery('FETCH_USERS', fetchUsers)
yield takeEvery('CREATE_USER', createUser)
}
监听未来的action,take
//使用take监听特定的action
import { take, put } from 'redux-saga/effects'
function* watchFirstThreeTodosCreation() {
for (let i = 0; i < 3; i++) {
const action = yield take('TODO_CREATED')
}
yield put({type: 'SHOW_CONGRATULATION'})
}
无阻塞调用fork
function* loginFlow() {
while(true) {
const {user, password} = yield take('LOGIN_REQUEST')
// fork return a Task object
const task = yield fork(authorize, user, password)
const action = yield take(['LOGOUT', 'LOGIN_ERROR'])
if(action.type === 'LOGOUT')
yield cancel(task)
yield call(Api.clearItem('token'))
}
}
同时执行多个任务
const [users, repos] = yield [
call(fetch, '/users'),
call(fetch, '/repos')
]
竞赛race
function* fetchPostsWithTimeout() {
const {posts, timeout} = yield race({
posts: call(fetchApi, '/posts'),
timeout: call(delay, 1000)
})
if (posts)
put({type: 'POSTS_RECEIVED', posts})
else
put({type: 'TIMEOUT_ERROR'})
}