文章目录
基本要求
- 在空白脚手架的基础上添加一个跳转页面,记为MainPage,用于实现点击计数功能
- 在MainPage中组合两个功能组件,分比记为MainToolComponent和MainContextComponet
- 在MainToolComponent中添加两个按钮,实现页面中“增加技术”和“减少计数”的事件监听
- 在MainContextComponet中展示当前计数结果信息
实现准备
涉及概念与技术
- 路由表:实现从入口界面跳转至指定功能界面
- 容器组件:装载功能组件
- 功能组件:接收页面参数,展示model数据,发布操作更新请求
- models:记录技术结果,提供记录结果修改方法
- dva/connect: dva框架中对原生Redux封装后的connect函数
- dva/router: dva框架中对原始router的封装,封装后可通过dispatch实现路由跳转
- redux-actions插件:简化创建Redux的Action过程
主要操作步骤
- 定义MainModel,并注册到主程序中
- 定义MainPage组件,并配置到主路由中
- 定义MainToolComponent和MainContextComponet,并组合到MainPage组件
- 实现MainModel的reducer方法,实现MainModel中的state数据更新
- 绑定MainModel到MainToolComponent,并在MainToolComponent中向MainModel中发送更新计数指令
- 绑定MainModel到MainContextComponet,并在MainContextComponet中展示从MainModel中获取的数据
实现结果
MainModel定义与注册
MainModel定义
src\models\mainModel.js
export default {
/**
* 当前数据模型的命名空间
* 1. 作为dispatch-action中的限定前缀
* 2. 作为当前模型在redux中的属性名
*/
namespace: 'mainModel',
//当前数据模型所管理的数据
state: {
count : 100, //变化值
name : "cyoubo" //不变值
},
//当前数据模型对数据操作函数
//namespace+函数名,可以构成dispatch-action中的type值
reducers: {
plus(state, action) {
return { ...state, count: state.count + action.payload };
},
subtraction(state, action) {
return { ...state, count: state.count - action.payload };
},
},
};
MainModel注册
src\index.js
import dva from 'dva';
import './index.css';
// 1. Initialize
const app = dva();
// 2. Plugins
// app.use({});
// 3. Model
// 此处一定要开启注册,以便dva将当前model注入到redux空间中
app.model(require('./models/mainModel').default);
// 4. Router
app.router(require('./router').default);
// 5. Start
app.start('#root');
MainPage组件定义与路由配置
MainPage组件定义
src\routes\MainPage\MainPage.jsx
import React, { Component, Fragment } from 'react'
import MainContext from '../../components/MainContext/MainContext'
import MainTool from '../../components/MainTool/MainTool'
/**
* 容器组件
* 1. 内部只负责组合功能组件
* 2. 对外需要于路由表挂接
*/
export default class MainPage extends Component {
render() {
return (
<Fragment>
<h2>this is count demo</h2>
<MainContext></MainContext>
<MainTool></MainTool>
</Fragment>
)
}
}
路由配置
src\router.js
import React from 'react';
import { Router, Route, Switch } from 'dva/router';
import IndexPage from './routes/IndexPage';
import MainPage from './routes/MainPage/MainPage'
/**
* 路由配置表构架函数
*/
function RouterConfig({ history }) {
return (
<Router history={history}>
<Switch>
<Route path="/" exact component={IndexPage} />
<Route path="/countDemo" component = {MainPage}></Route>
</Switch>
</Router>
);
}
export default RouterConfig;
定义功能组件并组合
MainTool
src\components\MainTool\MainTool.jsx
import { Button } from 'antd'
import { connect } from 'dva'
import React, { Fragment } from 'react'
import * as actions from "../../action/MainModelActions"
/**
* 计数器案例-按钮栏功能组件
* @param {*} param0 由dva/connect绑定后,props属性中包含注入分发器dispatch
*/
function MainTool(props) {
//监听事件
function onBtnPlusClick() {
/**
* 1. 发布计数器更新消息
* 2. action由redux-actions创建
* 3. plusAction的参数表示action所携带的参数,默认记为playload
* 4. 此句等效于dispatch({type: "mainModel/plus", payload : 1})
*/
props.dispatch(actions.plusAction(1))
}
function onBtnSubtraction() {
props.dispatch(actions.subtractionAction(1))
}
return (
<Fragment>
<Button.Group>
<Button type = "primary" onClick = {onBtnPlusClick}>plus one</Button>
<Button type = "primary" onClick = {onBtnSubtraction}>subtraction one</Button>
</Button.Group>
</Fragment>
)
}
/**
* 1. 通过dva/connect高阶函数进行绑定,使得当前组件于redux托管空间发生关联
* 2. connect后,当前组件的props属性中自动包含dispatch方法,用于发布消息
* 3. connect函数没有参数,表示当前组件只是消息发布者,不是数据使用者
*/
export default connect()(MainTool)
MainContext
src\components\MainContext\MainContext.jsx
import { connect } from 'dva'
import React from 'react'
/**
* 计数器案例-按钮栏功能组件
* @param {*} param0 由dva/connect绑定后,props属性中包含注入的数据值和分发器
* 注意:此处在对传入的props时进行了解构,只取其中的data属性
*/
function MainContext({data}) {
return (
<h4>
{`current count is ${data.count}`} <br></br>
{`current name is ${data.name}`}
</h4>
)
}
/**
* 提取函数:告知connect函数,如何从redux空间中获取当前组件需要的数据
* @param {}} reduxState 整个redux托管的数据空间
*/
const maps = (reduxState) => {
//从redux空间中取出mainModel的数据数据,包裹成{data: {实际值}}的对象,传入到当前组件的props中
return { data : reduxState.mainModel}
}
/**
* 1. 通过dva/connect高阶函数进行绑定,使得当前组件于redux托管空间发生关联
* 2. connect后,当前组件的props属性中自动包含dispatch方法,用于发布消息
* 3. connect函数有参数,表示当前组件即是消息发布者,也是数据使用者
*/
export default connect(maps)(MainContext)
入口程序监听跳转
src\routes\IndexPage.js (样式文件省略)
import React from 'react';
import { connect } from 'dva';
import styles from './IndexPage.css';
import { routerRedux } from 'dva/router'
function IndexPage({dispatch}) {
//监听点击事件实现路由跳转
function onUlliClickHandle(event){
event.preventDefault();
const routerKey = event.target.dataset["key"]
//路由跳转通过还是通过dispatch发布消息实现
//此时的action由dva/router库中的routerRedux函数生成
dispatch(routerRedux.push(`/${routerKey}`))
}
return (
<div className={styles.normal}>
<h1 className={styles.title}>Yay! Welcome to dva!</h1>
<ul className = {styles.list} onClick = {onUlliClickHandle}>
<li><a href = "#" data-key = "countDemo">Count Demo</a></li>
</ul>
</div>
);
}
IndexPage.propTypes = {
};
//绑定后,当前组件的props参数自动包含dispatch函数
export default connect()(IndexPage);
应用总结
Model注册与使用
- 所有的model都应该在index.js中进行注册,否则不能将model的state属性托管到redux中
- Model中的reducer都是为了修改当前model的state参数而执行的
组件组合
Model与组件通讯
- dva/connect函数的本质是将当前组件与整个redux托管空间进行了绑定,而不是和某一个特定的Model进行绑定
- 功能组件发布消息时,至需要通过调用props中的distach方法即可,向redux发送action
- 功能组件展示数据时,需要单独配置映射对象,告诉当前组件如何从redux空间中取得需要的数据(而不是从特定的model中取得数据)