美文网首页react
redux-saga的render优化

redux-saga的render优化

作者: 你的时间非常值钱 | 来源:发表于2019-10-16 15:11 被阅读0次

    当我们使用redux-saga(redux中间件)时,数据更新流程是,dispatch带上action,交给reducers根据旧state生成新state(中间可以存在effects),订阅了相应model的组件(被connect)会发生数据(props)更新
    在谈到react相关优化时,常见的优化手段是减少不必要的re-render,我在好几个项目中碰到过同一个情况,就是一个组件的数据源需要多个接口的返回值来更新。例如我在服务监控组件要同时拉取(基本信息,签名图片,行车轨迹)的接口,不算初次挂载的render,就要至少多三次render。

    // 改造前model.js
    ...
    effects: {
        *getServiceInfo(_, { call, put, select }) {
          const orderCode = yield select(_ => _.order.orderCode)
          const res = yield call(getServiceInfo, { orderCode })
            if (res.state === '1') {
            yield put({
              type: 'updateState',
              payload: {
                serviceInfo: res.result,
              }
            })
          }
        },
        *getOrderSignImg({ payload }, { call, put }) {
          const res = yield call(getOrderSignImg, payload)
          if (res.state === '1') {
            yield put({
              type: 'updateState',
              payload: {
                signImg: res.result.imgUrl
              }
            })
          }
        },
        *getLocationTrace({ payload }, { call, put }) {
          const res = yield call(getLocationTrace, payload)
          if (res.state === '1') {
            yield put({
              type: 'updateState',
              payload: {
                trace: res.result,
              }
            })
          }
        },
    ...
    },
    subscriptions: {
    
    ...
    dispatch({
        type: 'getServiceInfo',
        payload,
    })
    dispatch({
        type: 'getOrderSignImg',
        payload
    })
    dispatch({
        type: 'getLocationTrace',
        payload,
    })
    ...
    

    上面是优化前的部分代码,我们看到三个dispatch分别传送三个effects,然后又分别传送给reducers,于是我的服务监控组件被更新了三次
    想想有没办法尽量减少render且能正确更新数据(就是一步到位render),曾想过一些方法:

    • 跪求后端大佬将接口把三个接口封装成一个接口
    • 只发一个dispatch,然后一个个yield call收集返回值,反正是同步写法,最后只yield put更新一遍就好了

    多个接口变成一个接口?

    假如能确保其他地方不会再有机会调用这里其中的一个接口,那我觉得是可以统一做成一个接口的,但实际开发情形是点击保存成功后只重新拉取基本信息的接口,明显其他更新是多余的,可见,接口功能粒度还是要看情形细化的,合并成一个接口行不通

    yield串行收集?

    一个个的请求按顺序发送,省略了一堆判断异常情况,2,3个串行还好,但多起来还是吃不消的

    // model.js
    ...
    *getAll({ payload }, { call, put })  {
          const r1 = yield call(getServiceInfo, payload)
          const r2 = yield call(getOrderSignImg, payload)
          const r3 = yield call(getLocationTrace, payload)
          ...
          
    }
    
    尝试优化
        *getAll({ payload }, { call, put }) {
          function *gen() {
            yield call(getServiceInfo, payload)
            yield call(getOrderSignImg, payload)
            yield call(getLocationTrace, payload)
          }
          for (let g of gen()) {
            console.log(g) 
          }
          // const r1 = yield call(getServiceInfo, payload)
          // const r2 = yield call(getOrderSignImg, payload)
          // const r3 = yield call(getLocationTrace, payload)
        }
    ...
    // 调用
     dispatch({
        type: 'getAll',
        payload,
    })
    
    打印结果.png

    创建一个生成器函数,企图想统一控制步骤,但打印发现都是call返回的描述对象,为什么不是期望直接一个我们常用的返回值呢?因为effect的内部是做了封箱处理,简单调用是得不到期望结果的,需通过dispatch(action)或者put(action)调用,网上叫拆箱。

    合并成一个dispatch?

    ...
    *getServiceInfo(_, { call, put, select }) {
          const orderCode = yield select(_ => _.order.orderCode)
          const res = yield call(getServiceInfo, { orderCode })
          // 改成返回Promise实例对象
          return new Promise(resolve => {
            if(res.state === '1') {
              resolve({
                serviceInfo: res.result,
              })
            }
          })
          // if (res.state === '1') {
          //   yield put({
          //     type: 'updateState',
          //     payload: {
          //       serviceInfo: res.result,
          //     }
          //   })
          // }
    },
    *getOrderSignImg(){},
    *getLocationTrace(){},
    ...
    // useage
    const apis = [
      'getServiceInfo',
      'getOrderSignImg',
      'getLocationTrace'
    ]
    // 用数组把返回结果(Promise)存起来
    const fetchs = apis.map(api => dispatch({ type: api, payload }))
    
    Promise.all(fetchs).then(data => {
      let state = {}
      for (const i in data) {
        state = { ...state, ...data[i] }
      }
      dispatch({
        type: 'updateState',
        payload: state,
      })
    })
    

    把effect相应的业务函数改写成返回Promise实例对象,方便给Promise.all处理,Promise任务全部完成用个对象存起来,一次dispatch更新。
    但回想前面提过的一个问题,就是其中一个effect会有单独调用的场景,现在都返回Promise对象怎么破,其实不难,判断一下(instanceof Promise)就好了。

    总结:本文利用Promise.all将所需effect组合,通过一个dispatch讲所有数据统一更新,目的是减少由model变化引起多余的re-render

    相关文章

      网友评论

        本文标题:redux-saga的render优化

        本文链接:https://www.haomeiwen.com/subject/bqesmctx.html