Redux-saga 学习

83 阅读4分钟

什么是redux-saga?

中间件的执行流程 :action -> Middlewares -> reducer

  • redux-saga 是一个用于管理应用程序副作用的 library,它的目的是让副作用管理更加的简单,执行更高效(作用
  • redux-saga 就是一个 redux 的中间件,可以通过正常的 redux action 从主应用程序启动,暂停和取消,它可以访问完整的 redux state ,也能够 dispatch redux action使用
  • redux-saga 使用了ES6的 Generator 功能 ,让异步流程更加易于读取,写入,测试,通过这种方式,让异步看起来更加像标准同步的js代码(本质

前端副作用

为什么使用redux-saga?

在处理这些副作用时,我们需要一种方式来控制它们的执行顺序,处理可能出现的错误,以及在需要的时候取消它们。这就是 redux-saga 和其他副作用管理库的用武之地

API介绍

注册使用

  • createSagaMiddleware(options) : 创建一个 Redux middleware ,并将Sagas连接到 Redux Store,通过 createStore 第三个参数传入
    • options: 传递给 middleware 的选项列表,默认可以不用传递
  • middleware.run(saga,...args) : 动态地运行saga,只能用于在 applyMiddleware 阶段之后执行Saga

监听action

  • takeEvery(pattern,saga,...args): 监听每一次Action,匹配到Action便会执行一次(所有)
    • pattern: 与 action 参数的 type 同名
    • saga: 回调函数
  • takeLatest(pattern,saga,...args) : 监听匹配的Action,每次触发,会取消掉上一次正在执行的异步任务,请求数据一般用这个(最后一个)
  • throttle(ms,pattern,saga,...args) : 匹配到一个对应的action后,会执行一个异步任务,但是同时还会接收一次对应action的异步任务,放在底层的buffer中,那么在第一个参数 ms 毫秒内将不会执行异步任务(第一个和第二个)
  • take(pattern) : 阻塞的方法,用来匹配发出的action
    • take 返回一个对象,为 action 的参数 image.png

其他方法

  • select() : 获取 reducer 的返回结果,如果调用的参数为则全都返回
    • 返回参数为经过 reducer 后的 return 值,虽然只触发了一个 reducer,但是会返回两个 image.png
  • call(fn,...args) : 创建一个阻塞任务的 Effect 。在Generator函数中使用 yield call 时,它会暂停Saga的执行,等待被调用函数执行完成,并将结果返回
    • fn 可以是一个 异步函数、生成器函数
  • fork(fn,...args) : fork是一个用于创建非阻塞任务的 Effect。与call不同,fork不会阻塞Generator函数的执行,而是会立即返回一个任务描述符,允许调用者继续执行后续的操作
  • cancel(task) : cancel 方法用于取消一个正在运行的任务。这是通过取消 generator 函数中正在运行的 Effect 来实现的。取消任务可以通过调用 yield cancel(task) 来完成,其中 task 是通过 forkspawn 创建的任务
  • cancelled() : cancelled 是一个用于检查任务是否被取消的函数。你可以在 generator 函数中使用 yield cancelled() 来获取一个布尔值,指示当前任务是否被取消
  • put(action) : 用来命令 middleware 向 Store 发起一个 action。这个 Effect 是非阻塞型的
  • race() : 用于创建一个竞速条件,它允许在多个 Effect 之间竞争,然后在其中一个 Effect 完成时终止其他 Effect
  • all([]...effects) : 合并saga监听函数(模块化)

为什么使用 while(ture){} 配合 take()

为了表示一个逻辑执行流程,利用 take() 的阻塞属性

登录注册为例: 在触发登录之后,执行登录逻辑,比如存用户信息,更新登录状态;之后才能触发退出登录的逻辑,所以退出登录的监听在登录之后

function* authorize(info) {
	try {
		const token = yield call(userLogin, info)
		yield put({ type: LOGIN_SUCCESS, token })
		// 保存 token
		yield call(saveToken, { token })
		return token
	} catch (error) {
		yield put({ type: LOGIN_FAIL});
	} finally {
		if (yield cancelled()) {
			// ... 修改登录状态
		}
	}
}

function* userSaga() {
	while (true) {
		const { payLoad } = yield take(LOGIN)
		const task = yield fork(authorize, payLoad);
		const action = yield take([LOGOUT, LOGIN_FAIL]);
		if (action.type === LOGOUT) {
			yield cancel(task);
		} 
		// 清除token
		yield call(clearToken())
	}
}

export default userSaga;

在多个 Effects 之间启动 race()

功能: 允许你同时启动多个任务,然后在其中一个任务完成时终止其他任务

应用场景: 可以用来控制一个请求的超时时间

function* fetchData() {
  try {
    const result = yield race({
      data: call(api.getData),
      timeout: call(delay, 5000) // 设置一个5秒的超时
    });

    if (result.data) {
      yield put({ type: 'FETCH_SUCCESS', payload: result.data });
    } else {
      yield put({ type: 'FETCH_TIMEOUT' });
    }
  } catch (error) {
    yield put({ type: 'FETCH_ERROR', error });
  }
}

组合 saga

  1. 使用 call() :同时执行多个任务
const [users, repos] = yield [
  call(fetch, '/users'),
  call(fetch, '/repos')
]
  1. 使用 race() :在多个 Effects 之间启动
  2. 使用 yield* :内置的 yield* 操作符来组合多个 Sagas,排列宏观任务使用示例

使用流程

  1. 注册使用
import reducer from './reducers';
import states from './states';
import actions from './actions';

import { applyMiddleware, legacy_createStore as createStore } from "redux";
import watchSaga from './sagas';
import createSagaMiddleware from 'redux-saga';

const sagaMiddleware = createSagaMiddleware();
// 用reducer实例化store
store = createStore(reducer,{},applyMiddleware(sagaMiddleware));
sagaMiddleware.run(watchSaga);
export default store;
  1. 编写 saga 文件,如果需要模块化则创建多个 saga 文件,使用 all() 合并再导出