React Saga能否解耦与Redux的硬编码绑定并实现await链式调用
核心问题1:代码片段的异步/同步函数区分
- 同步函数:
requestApiData():普通的Action Creator,纯同步逻辑,仅返回标准Action对象dispatch(requestApiData()):Redux的dispatch本身是同步方法,仅负责将Action投递到中间件队列,执行过程无异步等待yield put(xxx):Redux Saga的Effect创建函数,同步返回Effect描述对象,实际Action派发由Saga运行时处理,函数本身是同步的
- 异步逻辑/异步函数:
userReceiveAll:Saga生成器函数,内部封装了Axios异步请求流程,由Saga调度执行,整体链路是异步的- Axios.get本身是返回Promise的标准异步函数
- 从dispatch触发Action到Saga完成请求的完整链路是异步的,只是原生dispatch没有返回Promise,无法直接感知异步状态
核心问题2:Saga+Axios与Reducer的解耦及链式调用实现
完全可以实现,行业内有两种成熟的落地方案:
方案1:封装Dispatch返回Promise,支持await/链式调用
改造Action Creator和Dispatch逻辑,让触发异步请求后可以拿到Promise回调,感知执行状态:
- 改造Dispatch封装,返回Promise
const mapDispatchToProps = (dispatch) => ({ fetchArticle: (params) => { return new Promise((resolve, reject) => { dispatch({ type: FETCH_ARTICLE, payload: params, // 传入Promise控制函数给Saga meta: { resolve, reject } }) }) } })
- 改造Saga请求逻辑,完成后触发Promise回调
function* fetchArticleSaga(action) { try { const { data } = yield call(axios.get, `/api/articles/${action.payload.id}`) // 请求成功返回数据给组件 action.meta.resolve(data) } catch (err) { action.meta.reject(err) } }
- 组件内即可用await或链式调用
async handleAddGoodClick() { // 等待获取Article数据 const article = await this.props.fetchArticle({id: 5}) // 存入对应业务Reducer this.props.addGoodToSale(article) // 调用后续异步计算逻辑 await this.props.recalculateSale() }
如果需要实现你要求的链式调用风格,可以进一步封装操作对象:
class ArticleOps { constructor(article, dispatch) { this.article = article this.dispatch = dispatch } addGoodToSale() { this.dispatch({ type: SAVE_ARTICLE_IN_SALE_ITEM, payload: this.article }) return this } async recalculateSale() { await new Promise((resolve) => { this.dispatch({ type: RECALCULATE_SALE, meta: { resolve } }) }) return this } } // 改造fetchArticle返回操作对象 const mapDispatchToProps = (dispatch) => ({ fetchArticle: async (params) => { const article = await new Promise((resolve, reject) => { dispatch({ type: FETCH_ARTICLE, payload: params, meta: { resolve, reject } }) }) return new ArticleOps(article, dispatch) } })
最终就能实现你期望的调用方式:
handleAddGoodClick() { this.props.fetchArticle({id:5}) .then(ops => ops.addGoodToSale().recalculateSale()) }
方案2:抽离独立API层,完全解耦请求和存储逻辑
把Axios请求逻辑完全抽成独立的公共工具层,不和任何Saga、Reducer绑定,各业务模块按需调用:
- 抽离公共API层
// /api/article.js 完全独立的请求逻辑 export const fetchArticleApi = (id) => axios.get(`/api/articles/${id}`).then(res => res.data)
- 各业务模块的Saga自行处理存储和后续逻辑
// 销售模块Saga function* addGoodToSaleSaga(action) { const article = yield call(fetchArticleApi, action.payload.id) yield put({ type: SAVE_ARTICLE_IN_SALE_ITEM, payload: article }) yield put(recalculateSale()) } // 采购模块Saga function* addGoodToPurchaseSaga(action) { const article = yield call(fetchArticleApi, action.payload.id) yield put({ type: SAVE_ARTICLE_IN_PURCHASE_ITEM, payload: article }) yield put(recalculatePurchase()) }
这种方案完全避免了硬编码分支判断,公共请求逻辑100%复用,各业务模块逻辑独立,可维护性最高。
内容的提问来源于stack exchange,提问作者TomR
相关产品推荐
相关产品推荐

