欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-05-31 19:48:14
前言 最近在给自己的脚手架项目转到TypeScript时,遇到了一些麻烦。 项目之前采用的是react + react redux + redux thunk + redux actions +redux promise的体系。 当项目转TypeScript时,react和react redux这种 ......

 前言

最近在给自己的脚手架项目转到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项目:脚手架项目