简介
redux-saga是一个redux中间件,用于处理redux的Effect副作用(异步获取数据,访问浏览器缓存等)。利用ES6的Generator函数,实现异步操作的同步编程,使业务逻辑结构更加清晰
常用API
Saga生成器
首先我们要创建一个saga中间件
import createSagaMiddleware from 'redux-saga'
//创建saga中间件
const sagaMiddleware = createSagaMiddleware()
复制代码
然后我们需要向redux注册中间件
import { createStore, applyMiddleware } from 'redux'
const store = createStore(
rootReducer,
initialState,
applyMiddleware([sagaMiddleware])
)
复制代码
最后我们需要把saga运行起来,监听store dispatch出来的action
import rootSaga from './saga'
sagaMiddleware.run(rootSaga)
复制代码
⚠️注意:运行saga中间件必须在注册之后
Saga辅助函数
- takeEvery:监听action,并执行相应的Generator函数(effect)如:
import { takeEvery } from 'redux-saga/effects'
import { ActionType } from './action'
function* login() {
//网络请求
}
function* rootSaga() {
const result = yield takeEvery(ActionType.LOGIN ,login)
}
复制代码
- takeLatest(最常用):对takeEvery做了防抖操作,即很短时间内触发多个action,只响应最后一个action。如
import { takeLatest } from 'redux-saga/effects'
import { ActionType } from './action'
function* login() {
//网络请求
}
function* rootSaga() {
const result = yield takeLatest(ActionType.LOGIN ,login)
}
复制代码
- throttle:对takeEvery做了节流操作,第一个参数表示延时时间(单位ms)。即第一个参数指定时间内触发多个请求,第一个请求正常发出,第二个请求进入buffer,后面的请求都不会响应。第一个请求响应完成后处理buffer中的请求。也就是说会有两个请求响应。如
import { throttle } from 'redux-saga/effects'
import { ActionType } from './action'
function* login() {
//网络请求
}
function* rootSaga() {
const result = yield throttle(200, ActionType.LOGIN ,login)
}
//200ms内的多个请求,第一个正常发起,第二个进入buffer
复制代码
- take:以上三个api都是先定义action和任务的对应关系,在每个action到来时执行对应的任务。take则不同,在saga运行【sagaMiddleware.run(rootSaga)】的时候,代码阻塞在take处,直到对应的action被触发,才开始继续往下执行。且是单次执行,即下次再触发action时并不会触发
function* loginFlow() {
//需要循环监听
while (true) {
yield take(ActionType.LOGIN, login)
// ... perform the login logic
yield take(ActionType.LOGOUT, logout)
// ... perform the logout logic
}
}
复制代码
Effect构建器
- call:用于发起effect请求,返回一条描述异步函数调用信息,配合yield调用异步函数并返回函数结果
import { call } from 'redux-saga/effects'
function* fetchProducts() {
const products = yield call(Api.fetch, '/products')
// ...
}
复制代码
- apply: 与call函数作用一样,调用方式同js的apply函数
yield apply(obj, obj.method, [arg1, arg2, ...])
复制代码
- fork: 原理与call和apply一样,但是call和apply是会阻塞的Effect,fork不会阻塞当前执行内容,可以继续往下执行,且fork返回一个task对象,可以被取消
import { fork, call, take, put } from 'redux-saga/effects'
import Api from '...'
function* authorize(user, password) {
try {
const token = yield call(Api.authorize, user, password)
yield put({type: 'LOGIN_SUCCESS', token})
} catch(error) {
yield put({type: 'LOGIN_ERROR', error})
}
}
function* loginFlow() {
while(true) {
const {user, password} = yield take('LOGIN_REQUEST')
yield fork(authorize, user, password);
//如果此处用call执行任务,在authorize执行未返回之前,程序都会被阻塞在这里,因而无法监听到后面的LOGOUT action,会导致UI和应用状态不一致的问题
yield take(['LOGOUT', 'LOGIN_ERROR'])
yield call(Api.clearItem('token'))
}
}
复制代码
- put: 同dispatch函数功能一样,用于发布一个同步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 })
}
复制代码
- select:用于获取store中的数据。当用户dispatch action时,saga先是透传数据给store reducer,然后在通知saga effect执行。如:
function* login() {
const user = yield select((state) => state.login.user)
try {
const res = yield call(API.login, user)
const data = yield res.json()
//执行完异步网络请求,派发action
yield put(Actions.login(data))
} catch (error) {}
}
function* rootSaga() {
//saga中间件拦截同步action
yield all([takeLatest(ActionType.LOGIN, login)])
}
复制代码
Effect组合器
- all:并行地运行多个 Effect,并等待它们全部完成。与Promise.all类似。如:
function* rootSaga() {
//saga中间件拦截同步action
yield all([takeLatest(ActionType.LOGIN, login)])
}
复制代码
- race: 与Promise的race一样,用于创建多个effect之间的竞赛运行。常用于对一些接口访问进行timeout限制
import { race, take, call } from 'redux-saga/effects'
function* backgroundTask() {
while (true) { ... }
}
function* watchStartBackgroundTask() {
while (true) {
yield take('START_BACKGROUND_TASK')
yield race({
task: call(backgroundTask),
cancel: take('CANCEL_TASK')
})
}
}
复制代码
基本用法
在介绍Saga生成器部分,我们已经介绍了基本用法,这里直接上代码吧
安装
$ yarn add redux react-redux redux-saga redux-logger redux-devtools-extension
复制代码
创建sagas
// src/store/sagas/login
import { call, put, takeEvery } from 'redux-saga/effects'
function* fetchData(action) {
try {
const data = yield call(Api.fetchUser, action.payload.url);
yield put({type: "FETCH_SUCCEEDED", data});
} catch (error) {
yield put({type: "FETCH_FAILED", error});
}
//此处的put相当于dispatch,都是发布一个同步action
}
export default function* () {
yield takeEvery('FETCH_REQUESTED', fetchData)
//每次触发同步FETCH_REQUESTED action时,执行fetchData函数,且允许多个fetchData函数同步执行
}
复制代码
// src/store/sagas/index
import watchFetchData from './saga';
export default function* rootSaga() {
yield all([
watchFetchData(),
...
])
}
复制代码
注册sagas
// store/index
import { createStore, applyMiddleware } from 'redux'
import createSagaMiddleware from 'redux-saga'
import rootReducer from './reducer.js'
import rootSaga from './saga'
const sagaMiddleware = createSagaMiddleware() //创建saga中间件
const bindMiddleware = (middleware) => {
if (process.env.NODE_ENV === 'development') {
const { composeWithDevTools } = require('redux-devtools-extension')
const { logger } = require('redux-logger')
middleware.push(logger)
return composeWithDevTools(applyMiddleware(...middleware))
}
return applyMiddleware(...middleware)
}
export default (initialState = {}) => {
const store = createStore(
rootReducer,
initialState,
bindMiddleware([sagaMiddleware])
)
sagaMiddleware.run(rootSaga)
return store
}
复制代码
参考:
redux-saga
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END