www
完成 UI 后,现在开始处理数据和逻辑。
dva 通过 model 的概念把一个领域的模型管理起来,包含同步更新 state 的 reducers,处理异步逻辑的 effects,订阅数据源的 subscriptions 。
上节导航切换时,打印的当前id,现在我们同步更新到model中。
在models文件夹下新建文件bzmain.js,并定义menuId状态,初始化为‘1‘,并在reducers中编写更新state的方法’changeData‘。
export default {
namespace: 'bzmain',
state: {
namespace: 'bzmain',
menuId: '1',
},
effects: {
},
reducers: {
'changeData'(state, {payload}) {
return {...state, ...payload}
},
},
};
修改index.js,载入这个model
// 3. Model
app.model(require('./models/bzmain').default);
如何将model和component关联起来,dva 提供了 connect 方法,就是 react-redux 的 connect 。
编辑 routes/BzMain.js,修改部分如下:
...
import {connect} from 'dva';
...
...
export default connect(({bzmain}) => ({
bzmain,
}))(BzMain);
...
handleMenuSelect方法中,接收到的id,来更新model中的menuId
...
function handleMenuSelect(id) {
dispatch({
type: 'bzmain/changeData',
payload: {
menuId: id,
},
})
}
...
BzMain.js中接收model中的状态
...
const BzMain = ({dispatch, bzmain}) => {
const {namespace, menuId} = bzmain;
...
并在Content展示当前id
...
当前的导航id:{menuId}
...
观察效果:
后面定义不同的子页面的时候,就通过这样来改变Content 中的内容了。
依样画葫芦,实现导栏的暗黑风格和亮白风格的切换,bzmain.js定义themeFlag主题标志;BzMain.js接收,定义方法handleSwitchChange,方法中根据接收参数改变bzmain.js的themeFlag,然后将themeFlag和方法传递给子组件BzMenu.js,在BzMenu.js中接收,并增加切换主题的开关,调用此方法,传递当前开关状态,并且还要根据接收的themeFlag开关状态,同步修改各元素的样式或属性,最终实现切换主题,正式项目中也可以多弄几个主题,把开关改成下拉列表进行主题选择。
另外,为了保证刷新页面能保存上次的themeFlag开关状态,我们也将其存在localStorage中,并在组件刷新时从localStorage读取。
bzmain.js
export default {
namespace: 'bzmain',
state: {
namespace: 'bzmain',
themeFlag: 'true',
menuId: '1',
},
effects: {
},
reducers: {
'changeData'(state, {payload}) {
return {...state, ...payload}
},
},
};
BzMain.js
import React from 'react';
import BzMenu from '../components/BzMenu';
import {Layout} from 'antd';
import style from './BzMain.less';
import {connect} from 'dva';
const {Header, Content, Footer} = Layout;
const BzMain = ({dispatch, bzmain}) => {
const {namespace, menuId} = bzmain;
const themeFlag = localStorage.getItem('themeFlag');
function handleSwitchChange(value) {
localStorage.setItem('themeFlag', value);
dispatch({
type: namespace + '/changeData',
payload: {
themeFlag: value,
},
})
}
function handleMenuSelect(id) {
dispatch({
type: namespace + '/changeData',
payload: {
menuId: id,
},
})
}
return (
当前的导航id:{menuId}
)
};
// export default BzMain;
export default connect(({bzmain}) => ({
bzmain,
}))(BzMain);
BzMenu.js:
这里增加了用PropTypes 做数据校验。
import React from 'react';
import {Col, Menu, Row, Switch} from 'antd';
import {AppstoreOutlined, UserOutlined} from '@ant-design/icons';
import style from './BzMenu.less';
import PropTypes from 'prop-types';
const BzMenu = ({onMenuSelect, onSwitchChange, themeFlag}) => {
const list = [{
key: '1',
data: '真元',
},{
key: '2',
data: '基础',
},{
key: '3',
data: '装备',
},{
key: '4',
data: '藏经阁',
},{
key: '5',
data: 'NPC查找',
},
];
return (
onSwitchChange(value)} checked={themeFlag}/>
);
};
BzMenu.propTypes = {
onMenuSelect: PropTypes.func.isRequired,
onSwitchChange: PropTypes.func.isRequired,
themeFlag: PropTypes.string.isRequired,
};
export default BzMenu;
BzMenu.less
:global {
.ant-menu-item {
.anticon-appstore {
margin-right: calc(~"100vw / 50 - 6px");
}
}
.ant-switch {
border-color: white;
}
}
.item {
padding: 0 calc(~"100vw / 50 - 2px");
}
.menu {
padding-left: calc(~"100vw / 5 - 60px");
}
.img {
height: 50px;
background-color: white;
padding: 5px;
}
通过Content下隐藏和显示,以此给出用户更换页面的体验。
在routes文件夹下,新建content文件夹,在content文件夹下新建各个子页面,分别为
Basics.js
import React from 'react';
import PropTypes from 'prop-types';
const Basics = ({hiddenFlag}) => {
return (
基础页面
);
};
Basics.propTypes = {
hiddenFlag: PropTypes.bool.isRequired,
};
export default Basics;
Energy.js
import React from 'react';
import PropTypes from 'prop-types