跳到主要内容

redux-thunkredux-saga

Redux 中,中间件(Middleware)用于处理副作用(如异步处理,日志记录等)。redux-thunkredux-saga 是最常用的两种处理异步的中间件(Middleware),他们在设计思路、使用方法和适用场景上有显著差异。

信息

redux-thunk 是”够用就好“的务实选择,而 redux-saga 是为了解决复杂问题而生的强大工具

一、 redux-thunk

1. 核心思想

redux-thunk 允许 action creator 返回一个函数(而不仅仅是一个对象),这个函数可以包含异步逻辑,并通过 dispatch 主动触发其他 action。本质是将异步逻辑封装在 action creator 中。

2. 安装和引入

npm install redux-thunk

在 Redux Store 中应用中间件:

import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers';

const store = createStore(rootReducer, applyMiddleware);

3. 编写异步 Action Creator

返回一个函数,接收 dispatchgetState 作为参数:

// 普通 Action (同步)
const fetchUserSuccess = user => ({
type: 'FETCH_USER_SUCCESS',
payload: user,
});
const fetchUserFailure = error => ({
type: 'FETCH_USER_FAILURE',
payload: error,
});

// 异步 action (使用 thunk)
const fetchUser = userId => (dispatch, getState) => {
// 先 dispatch 加载状态
dispatch({ type: 'FETCH_USER_PENDING' });

// 模拟异步请求
fetch(`https://api.lmssee.com/users/${userId}`)
.then(res => res.json())
.then(user => dispatch(fetchUserSuccess(suer)))
.catch(error => dispatch(fetchUserFailure(error)));
};

// 触发 异步 action
store.dispatch(fetch(123));

4. 特点

  • 简单轻量:仅需封装函数,学习成本低
  • 逻辑分散:异步逻辑直接写在 action creator 中,可能导致组件或 action 文件臃肿
  • 灵活性低:难以处理复杂流程(如竞态条件、取消请求、并发控制)

二、 redux-saga

redux-saga 使用 生成器函数(Generator)来管理副作用,通过监听特定 action 触发任务,将异步逻辑集中到独立的 saga 文件中,实现更可控的副作用管理。

1. 安装与配置

import { createStore, applyMiddleware } from 'redux';
import createSagaMiddleware from 'redux-saga';
import rootReducer from './reducers';
import rootSage from './sagas';

const sagaMiddleware = createSagaMiddleware();
const store = createStore(rootReducer, applyMiddleware(sagaMiddleware));

sagaMiddleware.run(rootSaga); // 启动 saga

2. 编写 Saga 任务

使用生成器函数定义任务。通过 yield 执行异步操作或派发 action:

import { takeEvery, put, call } from 'redux-sage/effects';

// 监听 FETCH_USER_REQUEST 动作
function* fetchUserSaga(action) {
try {
// 调用异步函数 (call effect)
const user = yield call(
fetch,
`https://api.lmssee.com/user/${action.payload}`,
);
const data = yield user.json();

// 派发成功 action (put effect 等价于 dispatch )
yield put({ type: 'FETCH_USER_SUCCESS', payload: data });
} catch (error) {
// 派发失败 action
yield put({ type: 'FETCH_USER_FAILURE', payload: error });
}
}

// 监听所有 FETCH_USER_REQUEST 动作(类似事件监听)
function* watchFetchUser() {
yield takeEvery('FETCH_USER_REQUEST', fetchUserSage);
}

// 根 sage (组合所有任务)
export default function* rooSaga() {
yield watchFetchUser();
// 添加其他任务,如 watchAnotherAction();
}

3. 触发动作

组件中只需要 dispatch 普通 action:

store.dispatch({ type: 'FETCH_USER_REQUEST', payload: 123 });

4. 核心概念

  • Effectredux-saga 通过 effect 描述副作用(如 call 调用异步函数, put 派发 action,takeEvery 监听 action),生成器通过 yield 执行这些 Effect
  • 生成器函数: 通过 function* 定义,yield 执行暂停,等待异步操作完成
  • 任务组合:可以通过 fork 并行任务, race 竞态条件,takeEvery 取消旧任务等

三、核心区别

纬度redux-thunkredux-saga
实现方式函数返回函数(闭包)生成器函数 + Effect 描述
异步逻辑位置封装在 action creator 中集中在独立的 saga 文件中
复杂度✅ 简单,学习成本低❌ 较复杂,需要理解生成器和 Effect
流程可控❌ 有限(难以处理竞态、取消等)✅ 强大(支持 racetakeLatestcancel
并发处理❌ 手动管理(Promise.all 等)✅ 内置强大支持(raceforkall 等)
可测试性❌ 需 mock dispatchgetState✅ 生成器输出纯值,易测试(无需真实测试)
调试✅ 相对简单,逻辑线形❌ 较复杂,但有 DevTools 支持
代码体积✅ 小(< 1KB)❌ 较大
适用场景简单异步(如单个 API 调用)复杂流程(如并发、取消、长轮询、状态机)

四、使用场景

1.选择 redux-thunk

  • 异步逻辑简单(如单个 API 请求,无复杂依赖)
  • 希望快速上手,避免额外学习成本
  • 项目规模小,不需要集中管理副作用
  • 项目追求快速开发和最小化依赖
  • 习惯使用 async/await

2.选择 redux-saga

  • 需要处理复杂异步流程(如竞态条件:同时发起多个请求,只取最后一个结果)
  • 需要取消未完成的请求(如用户离开页面时取消 API 调用)
  • 异步逻辑需要复用或集中管理(如多个组件触发同一异步任务)
  • 需要持久化副作用任务(如页面刷新后恢复任务)
  • 项目规模大,需要更好的可维护性和可测试性

3. 现代选择

随着 React Hook 和 useReducer + useEffect 的普及,以及像 「RTK Query」(Redux Toolkit 自带)这样的现代化的解决方案出现,许多新项目已经不再使用 redux-thunkredux-saga 。「RTK Query」提供了开箱即用、自动更新获取等功能,极大地简化了数据获取。但对于需要精细控制复杂副作用的场景。 redux-saga 依然有其不可替代的价值。