功能
dva流程简单来说就是model层和view层的分离,数据的存储不放在组件的state中,单独拎出来放在model中。项目开发过程中个人感觉全局性数据(比如用户名,用户权限)可以采用这种方法实现。
本例中page层页面List.js负责显示,数据和更新数据的方法放在model层中的DataList.js中。实现效果如下:
代码
- page层:src/routes/List.js
import React from 'react';
//dva是1种分层想,将page(routes/List.js)和model(models/ListData.js)进行分层
//其中page只负责页面显示,数据在model中进行处理
import {
connect} from 'dva';
const namespace = 'list';
//说明:第1个回调函数将page层和model层进行连接,返回model中的数据
//并且将返回的数据绑定到this.props中,下面通过this.props.dataList拿数据
//注意不是this.state.dataList
//第2个回调函数,将定义的函数绑定到this.props中,可以调用model层中定义的函数
//通过this.props.add()操作,不是this.state.add()
//*******************以下是第1种写法,直接把2个回调函数写入*******************
//dva的作用是connect model和page
//2个回调函数作为参数
//参数1:将page和model进行连接,返回modle中的数据,并且将返回的数据绑定到this.props
//参数2:将返回的函数绑定到this.props
@connect((state) => {
//state是整个项目全局的state,通过命名空间获取指定的model的state
return {
//返回1个对象,将返回的值绑定到this.props中
dataList: state[namespace].data,
maxNum: state[namespace].maxNum
}
}, (dispatch) => {
//dispatch的作用:可以调用model层定义的函数
return {
//将返回的函数,绑定到this.props中
add() {
dispatch({
type: 'list/addNewData' //model的namespace + model中定义的函数名
});
},
init: () => {
//箭头函数写法
dispatch({
type: 'list/initData'
});
}
//下面可以定义多个函数
}
})
//*******************以上是第1种写法*******************
//*******************以下是第2种写法*******************
// const mapStateToProps = (state) => {
// return {//返回1个对象
// dataList: state[namespace].data,
// maxNum: state[namespace].maxNum
// }
// };
//
// const mapDispatchToProps = (dispatch) => {
// return {//将返回的函数,绑定到this.props中
// add() {
// dispatch({
// type: 'list/addNewData' //model的namespace + model中定义的函数名
// });
// },
// init() {
// dispatch({
// type: 'list/initData'
// });
// }
// //下面可以定义多个函数
// }
// };
//
// @connect(mapStateToProps, mapDispatchToProps)
//*******************以上是第2种写法*******************
class List extends React.Component {
//构造函数其实没用,因为props都是从model中获取
// constructor(props) {
// super(props);
//
// //初始化数据分离到model层
// this.state = {
// dataList: [1, 2, 3],
// maxNum: 3
// }
// }
//生命周期函数
componentDidMount() {
//组件加载完成后初始化操作
this.props.init();
}
render() {
return (
<div>
<ul>
{
this.props.dataList.map((value, index) => {
return <li key={
index}>{
value}</li>
})
}
</ul>
<button onClick={
() => {
this.props.add();
}}>点击
</button>
</div>
)
}
}
export default List
- model层:src/models/listData.js
//effects中的request对象
import request from "../utils/request";
export default {
namespace: 'list', //src/models中所有的model通过namespace进行区分
state: {
//state传入下面的addNewData函数
data: [],
maxNum: 1
},
//新增effects配置,用于异步加载数据
effects: {
//initData在route层页面加载后调用,componentDidMount中调用
* initData(params, {
call, put}) {
//定义异步方法(*表示异步方法 ),获取到call,put方法
const url = '/ds/list'; //定义请求的url,具体初始化数据在mock中,注意开始有反斜杠/,和mock对应
//执行请求,result表示拿到的数据,call中的参数request就是导入的utils/request.js
let result = yield call(request, url);
yield put({
//调用reducers中的方法,传递到addNewData里面的result参数
type: 'addNewData', //指定方法名,调用reducers中的addNewData
data: result.data, //传递ajax回来的数据,后面的result是ajax拿到的数据,前面的data是定义的state属性名
maxNum: result.maxNum
});
}
},
//model层新增reducer方法,用于更新state中的数据
reducers: {
addNewData(state, result) {
//参数state指的是更新之前的state数据,result代表ajax请求到的数据
if (result.data) {
//如果state中存在data数据,直接返回,在做初始化的操作
return result.data; //返回更新后的数据
}
let maxNum = state.maxNum + 1;
let newArr = [...state.data, maxNum]; //形成新的数组
//更新状态数据
return {
//通过return返回更新后的数据,不是setState
data: newArr,
maxNum: maxNum
}
}
}
}
- 路由src/router.js
import React from 'react';
import {
Router, Route, Switch} from 'dva/router';
import List from './routes/List';
function RouterConfig({
history}) {
return (
<Router history={
history}>
<Switch>
<Route path="/" exact component={
List}/>
</Switch>
</Router>
);
}
export default RouterConfig;
- request对象:src/utils/request.js
import fetch from 'dva/fetch';
function parseJSON(response) {
return response.json();
}
function checkStatus(response) {
if (response.status >= 200 && response.status < 300) {
return response;
}
const error = new Error(response.statusText);
error.response = response;
throw error;
}
/**
* Requests a URL, returning a promise.
*
* @param {string} url The URL we want to request
* @param {object} [options] The options we want to pass to "fetch"
* @return {object} An object containing either "data" or "err"
*/
export default function request(url, options) {
return fetch(url, options)
.then(checkStatus)
.then(parseJSON)
.then(data => ({
data }))
.catch(err => ({
err }));
}
- mock数据:mock/MockListData.js
export default {
"GET /ds/list": function (req, res) {
//模拟请求返回数据
res.json({
data: [1, 2, 3, 4],
maxNum: 4
});
}
}
- 开启mock: .roadhogrc.mock.js
export default {
...require('./mock/MockListData')
};
整体代码:https://github.com/nuptcheng/dva-list-demo.git