使用Redux顺序网络请求



  • 1.onPress()触发一个action执行网络请求,在componentWillReceiveProps(props)方法中得到返回结果,然后在componentWillReceiveProps()方法中根据结果判断是否触发第二个action执行网络请求;
    2.问题:使用Redux这种操作是否合理?由于触发第二个action后状态更新还是会调用componentWillReceiveProps(),因为第一个action的State未改变导致还会继续触发第二个action,该怎么解决?(现在的做法是如果第一个触发第二个action就清除第一个action对应State的状态)



  • @sirhu11 说:

    componentWillReceiveProps
    为什么要在componentWillReceiveProps这个方法里面做,可以在第一个action回调函数里面做



  • 首先对这个问题表示关注,希望能够有大厂的同学来介绍一下你们最佳的 Best Practice。

    我在使用 redux 的过程中也遇到了一样的问题,我们以官方的 Reddit API 为例,redux store 中保存的 object 结构如下(flow 写法):

    {
      isFetching: boolean,
      didInvalidate: boolean,
      items: Array<any>,
      lastUpdated: number,
    }
    

    然后通过 connect 函数,map store 中的这个 object 到对应的 component,然后在 componentWillReceiveProps 中处理后续连锁调用逻辑,按照你的问题 2,如果你两个请求在 store 中是相互独立的,应该不会出现这个问题。

    这样的实现带来的问题就是 componentWillReceiveProps 变得异常的长、繁琐,对比 Promise 写法,非常的不直观。如果还要处理请求错误的情况呢?

    目前的想法是在请求的 action 中带上 onSuccessonFailure 两个 field,利用 redux-sagas 监听 action 的调用情况,然后在 sagas 里调用对应函数,但此方法前提是你需要将网络请求类的 actions 合并到一个统一 action names 中。



  • 可以参考这里 https://github.com/markerikson/redux-ecosystem-links/blob/master/middleware-network-requests.md
    利用 middleware 来实现更复杂的逻辑



  • 逻辑中出现多个异步操作,强烈使用中间组件,如redux-saga,需要一点时间学习一下,但是会让所有异步请求变得像async/await一样简单。



  • 关注+1

    最近刚开始使用saga,大概如下,不知道我用的方式对不对

    /* ******** 请求用户信息 ******** */
    export function* watchRequestUserInfo() {
      // 1
      yield takeEvery(G_REQUEST_USERINFO, requestUserInfo);
    }
    
    function* requestUserInfo() {
      const { error, json } = yield call(
        fetchx.get,
        { url: `${baseUrls.showing}${apiPath.userInfo}` },
      );
    
      if (!error && json && json.code === 0) {
        // 2
        yield put({
          type: G_REQUEST_USERINFO_SUCCESS,
          payload: { userInfo: json.data },
        });
      } else {
        const err = error || new Error((json && json.message) || '请求用户信息失败');
        yield put({
          type: G_REQUEST_USERINFO_FAILURE,
          payload: { error: err },
        });
      }
    }
    

    第1处:(疑问) 现在用的是takeEvery,不知道就这个场景是不是takeLatest更合适?
    第2处:(题主的问题)一般我处理后面请求依赖前面请求的话会在这里做,不知道合理不?
    另外:saga支持请求协同么?如果可以该怎么做?(有的页面需要大量请求,想等所有请求完毕后,再去更新界面,减少不必要的更新)



  • @HermitCarb
    1、多数情况,使用takeLatest比takeEvery好,尤其是通过类似点击、下拉这种方式产生的请求,可能存在连续点击下拉,takeLatest的逻辑是当上一次没有执行完,又产生的了新的,那么老的请求和代码逻辑会继续执行,但是yield put的动作会被抛弃。也就是说,如果多次点击takeEvery会造成多次render,而takeLatest只会接收到最后一次
    2、如果一个动作需要多个请求来响应,且这些动作需要按照顺序执行,那么最好放在一个里面。
    3、不知道你问的是不是同时产生并发请求,如果是,那么可以,但是和saga无关,这个是由promise.all解决的。

    ps:yield 后面使用call调用函数还是直接yield func(xxx),主要在于,如果后面调用的动作里没有yield put,那么怎么调用都行。这时完全可以把yield 看做await,而且后面和await也一样,只要是promise就行。那么问题3里,只要 const [a,b,c] = yield Promise.all([fetchx1,fetchx2,fetchx3]) 就好了
    ps2:看你在严格的遵守redux建议的type命名,我这两年的经验是,如果你的项目不是很大,不需要特别多人(10人以上),这个东西完全没有必要专门定义,一起商量一下用字符串就行了,不然感觉太麻烦。
    ps3:真正项目中需要使用脚手架协助,不然太多小文件,正在考虑把我们自己写的脚手架开源出来,到时分享一下。



  • @whyming takeLatest、Promise.all豁然开朗,谢谢

    我项目下redux的结构大概是这样
    0_1522377494538_image.png
    现在项目就我自己,目前自定义的redux#action#type大概会有100多,主要是感觉这样定义type感觉会严格一点,加上eslint提示,写错了可以很方便提示出来



  • @HermitCarb
    这样也没问题,看项目具体情况吧,我们是仿照dva,自己写的一个简单脚手架。