Sagas
Sagas
我们可以理解为 Sage 是一个可以用来处理复杂的异步逻辑的模块,并且由 redux 的 action 触发。副作用就是在 action 触发 reduser 之后执行的一些动作,这些动作包括但不限于,连接网络,io 读写,触发其他 action。并且,因为 Sage 的副作用是通过 redux 的 action 触发的,每一个 action,sage 都会像 reduser 一样接收到。并且通过触发不同的 action, 我们可以控制这些副作用的状态,例如,启动,停止,取消。
基础使用
在定义生成 store 的地方,引入并加入 redux-sage 中间件。
// npm install --save redux-saga
import { createStore, applyMiddleware } from "redux";
import createSagaMiddleware from "redux-saga";
import reducer from "./reducers";
import mySaga from "./sagas";
// create the saga middleware
const sagaMiddleware = createSagaMiddleware();
// mount it on the Store
const store = createStore(reducer, applyMiddleware(sagaMiddleware));
// then run the saga
sagaMiddleware.run(mySaga);
副作用
副作用,顾名思义,在主要作用(action 触发 reducer)之外,用来处理其他业务逻辑。redux-saga 提供了几种产生副作用的方式, 主要用到了有两种 takeEvery 和 takeLates。
-
takeEvery 类似于 redux-thunk 的作用,会在接到相应的 action 之后不断产生新的副作用。比如,做一个计数器按钮,用户需要不断的点击按钮,对后台数据更新,这里可以使用 takeEvery 来触发。
-
takeLatest 在相同的 action 被触发多次的时候,之前的副作用如果没有执行完,会被取消掉,只有最后一次 action 触发的副作用可以执行完。比如,我们需要一个刷新按钮,让用户可以手动的从后台刷新数据,当用户不停单机刷新的时候,应该最新一次的请求数据被刷新在页面上,这里可以使用 takeLatest。
在下面的事例中,当我们点击 Fetch 按钮时,其会触发某个 FETCH_REQUESTED
动作,然后 Sagas 中的监听函数会自动去监听该动作并且触发数据加载:
import { call, put } from "redux-saga/effects";
import { takeEvery } from "redux-saga";
export 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 });
}
}
function* watchFetchData() {
yield* takeEvery("FETCH_REQUESTED", fetchData);
}
注意,takeEvery 第一个参数可以是数组或者方法。也可以有第三个参数用来传递变量给方法。takeEvery 会允许同时创建多个 fetchData 实例,这也就意味着可能某个时刻,某个 fetchData 在被执行的时候,它还有多个 fetchData 的动作尚未完成。如果我们只希望展示最新的数据请求的结果,则应该使用 taskLatest:
import { takeLatest } from "redux-saga/effects";
function* watchFetchData() {
yield takeLatest("FETCH_REQUESTED", fetchData);
}
takeLatest 同一时刻仅允许单个 fetchData 的任务运行,如果某个先前的任务尚未执行完毕,则其会被自动地终止。我们也可以同时创建多个监听函数:
import { takeEvery } from 'redux-saga/effects'
// FETCH_USERS
function* fetchUsers(action) { ... }
// CREATE_USER
function* createUser(action) { ... }
// use them in parallel
export default function* rootSaga() {
yield takeEvery('FETCH_USERS', fetchUsers)
yield takeEvery('CREATE_USER', createUser)
}