当前位置: 移动技术网 > IT编程>开发语言>JavaScript > 使用dva改造React旧项目的数据流方案

使用dva改造React旧项目的数据流方案

2019年12月14日  | 移动技术网IT编程  | 我要评论

 前言

最近在给自己的脚手架项目转到typescript时,遇到了一些麻烦。

项目之前采用的是react + react-redux + redux-thunk + redux-actions +redux-promise的体系。

当项目转typescript时,react和react-redux这种完美转换。

redux-actions转换也初步完成,但是各种为了适应typescript的声明很奇怪, 并且有些类型推断错误。

百度了一下,用typesafe-actions去替换,然后与redux-thunk 和redux-promise结合后,各种typescript类型错误。

这些类型推断稀奇古怪,网上也没有这个技术体系的相关文章,下班后调试代码,内心逐渐崩溃。

于是有了接下来的举措,用dva去替换react-redux + redux-thunk + redux-actions +redux-promise的数据流方案。

 关于dva

关于dva的介绍咱们就不说了,这里给个链接:。

明白它是基于 redux + react-router + redux-saga 的封装就够了。

我之前的脚手架是将action,reducer+initialstate 分成了不同的文件来处理。

而dva将 reducer, initialstate, action, saga 这些数据流相关的都放在model中一起处理。

 安装dva

网上很多都是安装dva-cli,然后再用 dva-quickstart。

然而我们是旧项目,只想用dva的数据流方案,所以不可能这么做,只需要安装dva就好了:

    npm i --save dva

博客发布时dva最新稳定版是:2.4.1。

 先看一下改造前的代码

在改造之前我们看一下原有入口js的代码:

import react, { suspense } from 'react';
import { createstore, applymiddleware } from 'redux';
import { provider } from 'react-redux';
import thunk from 'redux-thunk';
import createlogger from 'redux-logger';
import promisemiddleware from 'redux-promise';
import reactdom from 'react-dom';
import { hashrouter as router, route, switch } from 'react-router-dom';
import reducer from 'store/reducers';
import './app.less';

import frame from 'modules/layout/frame'

function loading() {
  return <div>loading...</div>;
}
const pagemain = react.lazy(() => import('modules/pagemain'));

const store = createstore(reducer, applymiddleware(thunk, createlogger, promisemiddleware));

const app = () => (
  <provider store={store}>
    <router>
      <suspense fallback={<loading />}>
        <frame>
          <switch>
            <route path='/' component={pagemain} />
          </switch>
        </frame>
      </suspense>
    </router>
  </provider >
);

reactdom.render(<app />, document.getelementbyid('app'));

改造入口js

先贴上改造后的代码:

import createlogger from 'redux-logger';
import dva from 'dva'

import routerconfig from './route/index'
import pagemainmodel from 'modules/pagemain/model'
import { createhashhistory } from 'history';

const app = dva({
  history: createhashhistory(),
  onaction: createlogger
});

app.model(pagemainmodel)

app.router(routerconfig)

app.start('#app')

首先我们新建了一个dva对象,这个dva对象设置了路由为hash路由,并且集成了redux-logger这个redux中间件。

更多参数配置可以查看:api文档

然后我们调用

app.model(pagemainmodel)

这一步主要是设置了数据流中数据的初始化和如何进行处理,具体后面会讲。

接着调用

app.router(routerconfig)

这一步dva设置了应用的路由,这是因为dva中集成了react-router-dom,所以可以进行路由设置。

当然接下来

app.start('#app')

也很好理解,对应原有代码中的 reactdom.render 。

改造路由部分

上面的代码比原有代码看起来精简很多,有一大半原因是我没有将route的配置提取出去。

所以我们先讲一下改造后路由 /route/index

import react, { suspense } from 'react'
import { router, switch, route } from 'dva/router'
import frame from 'modules/layout/frame'

function loading() {
  return <div>loading...</div>;
}

const pagemain = react.lazy(() => import('modules/pagemain'));

const routerconfig = (({ history }) => (
  <router history={history}>
    <suspense fallback={<loading />}>
      <frame>
        <switch>
          <route path="/" >
            <pagemain />
          </route>
        </switch>
      </frame>
    </suspense>
  </router>
));

export default routerconfig;

因为集成的是react-router-dom,所以基本上不需要改变,只需要按照dva.router的格式修改即可。

这里需要注意的是dva集成的react-router-dom是v4.1.2版本,还不能识别这种react.lazy这种最新的懒加载方式。

所以要将配置在route组件的component参数上的懒加载组件pagemain,修改为route组件的子组件,否则会报错。

改造reducer

首先看下咱们原有的代码:

import { handleactions } from 'redux-actions';
import * as t from './actiontypes';


const initialstate = {
  funddatas: []
};

const pagemainreducer = handleactions({
  [t.get_data_list]:
  {
    next(state, action) {
      if (!action.payload.data.data) {
        return {
          ...state,
          funddatas: []
        }
      }
      return {
        ...state,
        funddatas: action.payload.data.data.lsjzlist.reverse().map(l => ({
          netvaluedate: l.fsrq,
          netvalue: l.dwjz,
          totalnetvalue: l.ljjz,
          dayofgrowth: l.jzzzl
        }))
      }
    },
    throw(state) {
      return state;
    },
  }
}, initialstate);

export default pagemainreducer;

这里t.get_data_list是action的type的文本。

然后看下改造后的代码

import { getfunddata } from 'services/fundservice'

export default {
  namespace: 'pagemainmodel',
  state: {
    funddatas: []
  },
  effects: {
    *getdatas({ payload }, { call, put }) {
      const { data } = yield call(getfunddata, payload);
      const funddatas = data.data.lsjzlist.reverse().map((l) => ({
        netvaluedate: l.fsrq,
        netvalue: l.dwjz,
        totalnetvalue: l.ljjz,
        dayofgrowth: l.jzzzl
      }))
      yield put({ type: 'refreshdatas', payload: funddatas });
    },
  },
  reducers: {
    refreshdatas(state, { payload }) {
      return {
        ...state,
        funddatas: payload
      }
    },
  },
};

dva集成的是redux-saga,所以有使用经验的应该比较懂。

namespace是命名空间,用作各个model的key。

state是初始化值。

effects主要用来处理异步操作。

reducer部分和原有reducer类似。

另外getfunddata的格式如下:

export const getfunddata = (params: igetfundparams) => {
  const { fundcode, startdate, enddate, pagesize } = params
  return axios.get(`http://localhost:8011/getlist?fundcode=${fundcode}&startdate=${startdate}&enddate=${enddate}&pagesize=${pagesize}`)
};

改造action

下面是原有action

import { createaction } from 'redux-actions';
import axios from 'axios'
import * as t from './actiontypes';

/**
* 获取基金数据
*/
export const getdatalist = createaction(t.get_data_list, (fundcode, startdate, enddate, pagesize) => {
  return axios.get(`http://localhost:8011/getlist?fundcode=${fundcode}&startdate=${startdate}&enddate=${enddate}&pagesize=${pagesize}`)
});

connect到相应组件后的调用方式为:

this.props.getdatalist(fundcode, startdate, enddate, pagesize)

然后我们经过之前的改造已经没有了不再需要action这个文件,使用的时候直接使用以下方式:

this.props.dispatch({
  type: 'pagemainmodel/getdatas',
  payload: { fundcode, startdate, enddate, pagesize }
})

这里的dispacth是dva的connect直接封装传递到组件的。

改造容器组件

先给改造后的代码:

import react from 'react';
import { connect } from 'dva';

const mapstatetoprops = ({ pagemainmodel }) => {
  return {
    funddatas: pagemainmodel.funddatas
  };
}

/**
* 首页
*/
@connect(mapstatetoprops)
export default class pagemain extends react.component {
  componentdidmount() {
    this.getlist()
  }
  // 获取基金数据
  getlist = () => {

    // ...

    this.props.dispatch({
      type: 'pagemainmodel/getdatas',
      payload: { fundcode, startdate, enddate, pagesize }
    })
  }

  render() {
    //...
  }
}

改造前的代码就不给了,因为涉及的改动比较少,只需要注意dva的conenct只需要将redux的state传到组件,不需要将调用action的函数传入即可。

总结

最开始改造的目的是为了ts,但是等改造完成再去看dva的相关示例时,发现dva官网推荐的项目示例中的model文件中都加了

// @ts-ignore

来隐藏文件中的ts报错。(感觉有点囧啊,不过整个改造的过程还是蛮多收获的)

我自己的方案是将model.js的后缀不修改为ts,项目会避开对js文件的转换和检测。

总的来说,我只是对自己做的脚手架项目进行了一个改造,如果旧项目较大的话,改造起来还是挺费劲的。

博客中的代码都或多或少进行了删减,具体的项目代码可以查看我的github项目:脚手架项目

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网