ReactJS入门之Model层

news2025/1/15 12:56:42

目录

一:分层

二:使用DVA进行数据分层管理

三:在model中请求数据

四:mock数据


一:分层

       上图中,左侧是服务端代码的层次结构,由 Controller Service Data Access 三层组成服务端系统:
  • Controller 层负责与用户直接打交道,渲染页面、提供接口等,侧重于展示型逻辑。
  • Service 层负责处理业务逻辑,供 Controller 层调用。
  • Data Access 层顾名思义,负责与数据源对接,进行纯粹的数据读写,供 Service 层调用。
上图的右侧是前端代码的结构,同样需要进行必要的分层:
  • Page 负责与用户直接打交道:渲染页面、接受用户的操作输入,侧重于展示型交互性逻辑。
  • Model 负责处理业务逻辑,为 Page 做数据、状态的读写、变换、暂存等。
  • Service 负责与 HTTP 接口对接,进行纯粹的数据读写。

二:使用DVA进行数据分层管理

      dva 是基于 redux redux-saga react-router 的轻量级前端框架。官网: https://dvajs.com/
      对于 dva 我们不做过多详细的讲解,我们只要做到能够使用起来就可以了。对于想要全面学习 dva 框架的同学可自 行研究。
     首先,我们先将 dva 框架引入进来,由于 umi dva 进行了整合,所以导入就变得非常简单了。

 confifig.js文件中进行配置:

export default {
plugins: [
 ['umi-plugin-react', {
      dva: true // 开启dva功能
  }]
]
};
        接下来,创建 model 文件,在 umi 中,约定在 src/models 文件夹中定义 model ,所以,在该文件夹下创建
ListData.js 文件:

 编写内容:

export default {
namespace: 'list',
   state: {
    data: [1, 2, 3],
    maxNum: 3
}
}
下面对 List.js 进行改造:
import React from 'react';
import { connect } from 'dva';

const namespace = 'list';
const mapStateToProps = (state) => {

const listData = state[namespace].data;
return {
     listData
};
};

@connect(mapStateToProps)
class List extends React.Component{

render(){
  return (
<div>
<ul>
{
// 遍历值
this.props.listData.map((value,index) => {
return <li key={index}>{value}</li>
})
}
</ul>
<button
onClick={()=>{ //为按钮添加点击事件
// let maxNum = this.state.maxNum + 1;
// let list = [...this.state.dataList, maxNum];
// this.setState({ //更新状态值
// dataList : list,
// maxNum : maxNum
// });
}}>
添加</button>
</div>
);
}
}

export default List;
测试:

 可以看到,效果是一样的。

流程说明:
1. umi 框架启动,会自动读取 models 目录下 model 文件,即 ListData.js 中的数据
2. @connect 修饰符的第一个参数,接收一个方法,该方法必须返回 {} ,将接收到 model 数据
3. 在全局的数据中,会有很多,所以需要通过 namespace 进行区分,所以通过 state[namespace] 进行获取数据
4. 拿到 model 数据中的 data ,也就是 [1, 2, 3] 数据,进行包裹 {} 后返回
5. 返回的数据,将被封装到 this.props 中,所以通过 this.props.listData 即可获取到 model 中的数据
刚刚只是将数据展现出来,如果点击按钮,需要修改 state 的值,怎么操作呢?
首先,在 model 中新增 reducers 方法,用于更新 state 中的数据:
export default {
namespace: 'list',
state: {
data: [1, 2, 3],
maxNum: 3
},
reducers : {
addNewData(state){ //state是更新前的对象
let maxNum = state.maxNum + 1;
let list = [...state.data, maxNum];
return { // 返回更新后的state对象
data : list,
maxNum : maxNum
}
}
}
}
接下来修改 List.js 新增点击事件:
import React from 'react';
import { connect } from 'dva';
const namespace = 'list';
const mapStateToProps = (state) => {
const listData = state[namespace].data;
const maxNum = state[namespace].maxNum;
return {
listData, maxNum
};
};
const mapDispatchToProps = (dispatch) => { // 定义方法,dispatch是内置函数
return { //返回的这个对象将绑定到this.props对象中
addNewData : () =>{ // 定义方法
dispatch({ // 通过调用dispatch()方法,调用model中reducers的方法
type: namespace + "/addNewData" // 指定方法,格式:namespace/方法名
});
}
}
}
@connect(mapStateToProps, mapDispatchToProps) //mapDispatchToProps:函数,将方法映射到
props中
class List extends React.Component{
render(){
return (
<div>
<ul>
{
// 遍历值
this.props.listData.map((value,index) => {
return <li key={index}>{value}</li>
})
}
</ul>
<button
onClick={()=>{this.props.addNewData()}}>
添加
</button>
</div>
);
}
}
export default List;
测试:

测试结果,和之前实现效果一样。
流程梳理如下:

 

三:model中请求数据

        前面我的数据是写死在 model 中的,实际开发中,更多的是需要异步加载数据,那么在 model 中如何异步加载数据 呢?
       首先,创建 src 下创建 util 目录,并且创建 request.js 文件,输入如下内容:(用于异步请求数据)
// import fetch from 'dva/fetch';

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 async function request(url, options) {
const response = await fetch(url, options);
checkStatus(response);
return await response.json();
}
然后,在 model 中新增请求方法:
import request from '../util/request';

export default {
namespace: 'list',
state: {
data: [],
maxNum: 0
},

reducers: {
addNewData(state, result) { //result就是拿到的结果数据
if(result.data){ //判断result中的data是否存在,如果存在,说明是初始化数据,直接返回
return result.data;
}

let maxNum = state.maxNum + 1;
let list = [...state.data, maxNum];
return { //更新状态值
data: list,
maxNum: maxNum
}
}
},
effects: { //新增effects配置,用于异步加载数据
*initData(params, sagaEffects) { //定义异步方法
const {call, put} = sagaEffects; //获取到call、put方法
const url = "/ds/list"; // 定义请求的url
let data = yield call(request, url); //执行请求
yield put({ // 调用reducers中的方法
type : "addNewData", //指定方法名
data : data //传递ajax回来的数据
});
}
}
}
改造页面逻辑:
import React from 'react';
import { connect } from 'dva';

const namespace = 'list';

const mapStateToProps = (state) => {
const listData = state[namespace].data;
const maxNum = state[namespace].maxNum;
return {
listData, maxNum
};
};

const mapDispatchToProps = (dispatch) => {

return {
addNewData : () =>{

dispatch({
type: namespace + "/addNewData"
});
},

initData : () => { //新增初始化方法的定义
dispatch({
type: namespace + "/initData"
});
}
}
}
@connect(mapStateToProps, mapDispatchToProps)
class List extends React.Component{
componentDidMount(){
this.props.initData(); //组件加载完后进行初始化操作
}
render(){
return (
<div>
<ul>
{
// 遍历值
this.props.listData.map((value,index) => {
return <li key={index}>{value}</li>
})
}
</ul>
<button
onClick={()=>{this.props.addNewData()}}>
添加
</button>
</div>
);
}
}

export default List;
测试:
测试结果,发现会报错,原因是返回的数据不是 json 导致,解析出错。
查看下请求:

 可以看到,返回的是html代码,所以会导致出错。

完整的List.js页面代码:

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

const namespace = "list";

// 说明:第一个回调函数,作用:将page层和model层进行链接,返回modle中的数据
// 并且,将返回的数据,绑定到this.props

// 接收第二个函数,这个函数的作用:将定义的函数绑定到this.props中,调用model层中定义的函数
@connect((state) => {
    return {
        dataList : state[namespace].data,
        maxNum : state[namespace].maxNum
    }
}, (dispatch) => { // dispatch的作用:可以调用model层定义的函数
    return { // 将返回的函数,绑定到this.props中
        add : function () {
            dispatch({ //通过dispatch调用modle中定义的函数,通过type属性,指定函数命名,格式:namespace/函数名
                type : namespace + "/addNewData"
            });
        },
        init : () => {
            dispatch({ //通过dispatch调用modle中定义的函数,通过type属性,指定函数命名,格式:namespace/函数名
                type : namespace + "/initData"
            });
        }
    }
})
class  List extends React.Component{

    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;

完整的ListData.js页面代码:

import request from '../util/request';

export default {
    namespace: 'list',
    state: {
        data: [],
        maxNum: 1
    },
    reducers : { // 定义的一些函数
        addNewData : function (state, result) { // state:指的是更新之前的状态数据, result: 请求到的数据

            if(result.data){ //如果state中存在data数据,直接返回,在做初始化的操作
                return result.data;
            }

            let maxNum = state.maxNum + 1;
            let newArr = [...state.data, maxNum];


            return {
                data : newArr,
                maxNum : maxNum
            }
            //通过return 返回更新后的数据
        }
    },
    effects: { //新增effects配置,用于异步加载数据
        *initData(params, sagaEffects) { //定义异步方法
            const {call, put} = sagaEffects; //获取到call、put方法
            const url = "/ds/list"; // 定义请求的url
            let data = yield call(request, url); //执行请求
            yield put({ // 调用reducers中的方法
                type : "addNewData", //指定方法名
                data : data //传递ajax回来的数据
            });
        }
    }
}

完整的request.js页面代码:

// import fetch from 'dva/fetch';

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 async function request(url, options) {
    const response = await fetch(url, options);
    checkStatus(response);
    return await response.json();
}

四:mock数据

umi 中支持对请求的模拟,由于我们现在没有真正的服务可以返回数据,所以才需要模拟。
在项目根目录下创建 mock 目录,然后创建 MockListData.js 文件,并且输入如下内容:
export default {
'get /ds/list': function (req, res) { //模拟请求返回数据
res.json({
data: [1, 2, 3, 4],
maxNum: 4
});
}
}
进行测试:
发现,可以正常返回数据了。
页面效果也正常了 :

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/117602.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Docker图文 | Docker、Dockerfile、Docker-compose、Docker镜像仓库操作 | 系统性学习 | 无知的我费曼笔记

Dokcer和Linux一样都是一些死命令&#xff0c;不必花费过多的时间去学习。 也不必花费过多时间专项地记忆&#xff0c;在实际运用中随用随取即可。 还不如将省下来的时间更多地花费在于培养思维上。 文章目录Docker1.初识Docker1.1.Docker意义1.1.1.应用部署的环境问题描述1.1…

Antd UI Switch组件 中 checked与defaultChecked踩坑记录

目录 需求分析 问题发现 解决方法 总结 需求分析 需求其实很简单&#xff0c;就是在对应的表格行内添加一个人switch 滑块&#xff0c;用于开启或关闭单点登录入口。只需要修改一下对应的表格&#xff0c;添加一个滑块组件&#xff0c;新增一个接口。于是菜鸡博主&#xf…

MySQL库的基本操作与数据库的备份

目录 1、什么是数据库 2、数据库的基本使用 <1>本地连接服务器 <2>创建数据库 <3>创建数据库实例 <4>字符集和校验规则 3、操纵数据库 <1> 查看数据库 <2> 显示创建语句 <3> 修改数据库 <4> 数据库删除 <5>…

遇到的问题

一、git 1. git push之前忘记git pull:需要指定如何协调不同的分支。 解决&#xff1a; (1) git config pull.rebase false (2) git status 用于查看在你上次提交之后是否有对文件进行再次修改 (3) git stash 将当前的工作状态保存到git栈 2. 删除本地分支 # 删除本地分支 git…

【C++初阶】C++的IO流

文章目录C语言的输入输出流CIO流C标准IO流C文件IO流stringstream的简单介绍所有的测试代码C语言的输入输出 C语言中我们用到的最频繁的输入输出方式就是scanf ()与printf()。 scanf(): 从标准输入设备(键盘)读取数据&#xff0c;并将值存放在变量中。printf(): 将指定的文字/字…

Python最适合做什么?

Python最适合做什么&#xff1f; 最近我在Reddit上讨论了为什么有人会使用Python而不是其他编程语言。这个讨论非常好&#xff0c;因此我想写一篇关于它的文章。 首先&#xff0c;让我告诉你我对Python的看法。Python是我喜欢的一种语言&#xff0c;可以用于各种各样的应用&a…

spark读取elasticSerach

搭建参考:我的这篇 elasticsearch搭建_我要用代码向我喜欢的女孩表白的博客-CSDN博客 为了方便测试&#xff0c;我们先建立个索引&#xff0c;如果没有索引&#xff0c;他也能插入&#xff0c;只是走的是默认插入格式。 不过虽然接触es已经4年了&#xff0c;但是在工作中&…

XSS攻击原理及预防方法

XSS攻击通常都是通过跨站指令代码攻击网站的后台漏洞。它和信息隐性代码攻击攻击的目标不同。前者是透过从Web前端输入信息至网站&#xff0c;导致网站输出了被恶意控制的网页内容&#xff0c;使得系统安全遭到破坏。而后者则是输入了足以改变系统所执行之SQL语句内容的字串&am…

【解决方案】一种简单且实用的化工厂人员定位系统

化工厂人员定位系统是推进我国安全生产状况持续稳定好转的有效载体&#xff0c;对化工行业的科学发展、安全发展起着重要的促进作用。 化工厂安全责任重于泰山&#xff0c;一旦发生事故后果不堪设想。目前&#xff0c;化工企业还存在着缺乏实时监督、缺乏主动干预、缺乏精准救援…

SpringCloud(9)— Elasticsearch聚合和自动补全

SpringCloud&#xff08;9&#xff09;— Elasticsearch聚合和自动补全 一 数据聚合 1.聚合的分类 聚合&#xff08;aggregations&#xff09;可以实现对文档数据的统计&#xff0c;分析&#xff0c;运算。常见的聚合有三种&#xff1a; 1.桶聚合&#xff08;Bucket&#x…

磁场传感器调研报告

目录 一.磁场传感器 二.磁场传感器工作原理 2.1霍尔效应原理 2.2霍尔传感器工作原理 三.磁场传感器分类介绍 3.1磁阻敏感器 3.2磁性液体加速度传感器 3.3磁性液体水平传感器 四.磁性传感器的应用 4.1汽车 4.2消费类电子产品 4.3工业智能控制和自动化 五、总结 一.…

iftop工具(网卡流量监控软件)的使用

直接运行iftop&#xff0c;不加任何参数 界面显示 界面上面显示的是类似刻度尺的刻度范围&#xff0c;为显示流量图形的长条作标尺用的。 中间的< >这两个左右箭头&#xff0c;表示的是流量的方向。 TX&#xff1a;发送流量 RX&#xff1a;接收流量 TOTAL&#xff1a;总…

open-local部署之后k8s的kube-scheduler挂掉问题

搭建一套k8s集群之后&#xff0c;本地存储化方案选择了阿里巴巴的open-local&#xff0c;没部署open-local&#xff0c;k8s 的kube-scheduler一切正常&#xff0c;只要按照官方文档部署了open-local&#xff0c;k8s的kube-scheduler就会挂掉&#xff0c;不是被kill掉&#xff…

Go的并发模型

Go的并发模型 文章目录Go的并发模型一、GO并发模型的三要素1.1 操作系统的用户空间和内核空间1.2 线程模型的实现&#xff08;1&#xff09;用户级线程模型&#xff08;2&#xff09;内核级线程模型&#xff08;3&#xff09;两级线程模型1.3 GO线程实现模型MPG二、什么是gorou…

选择题

目录 1058:选择题 输入格式&#xff1a; 输出格式&#xff1a; 输入样例&#xff1a; 输出样例&#xff1a; 代码长度限制: 时间限制: 内存限制: 思路: 1.选择题结构体 1.2 选择题结构体代码 2.判断选择题是否做对函数 2.1 判断选择题是否做对函数代码: 3.选择题的输入…

【并发】深入理解JMM并发三大特性(二)

t【并发】深入理解JMM&并发三大特性&#xff08;二&#xff09; 我们在上一篇文章中提到了JMM内存模型&#xff0c;并发的三大特性&#xff0c;其中对可见性做了详细的讲解&#xff01; 这一篇文章&#xff0c;将会站在硬件层面继续深入讲解并发的相关问题&#xff01; …

将整数字符串转成整数值

题目&#xff1a; 给定一个字符串 str&#xff0c;如果str符合日常书写的整数形式&#xff0c;并且属于 32 位整数的范围&#xff0c;返回 str 所代表的整数值&#xff0c;否则返回 0 。 举例&#xff1a; str "123" 返回 123 str "023" 返回 23 …

springboot整合之统一异常处理

特别说明&#xff1a;本次项目整合基于idea进行的&#xff0c;如果使用Eclipse可能操作会略有不同&#xff0c;不过总的来说不影响。 springboot整合之如何选择版本及项目搭建 springboot整合之版本号统一管理 springboot整合mybatis-plusdurid数据库连接池 springboot整合…

FFmpeg简单使用:过滤器 ---- h264_mp4toannexb

H264有两种封装方式&#xff1a;字节流AnnexB格式 AVCC格式。 1. AnnexB格式 ---- 用于实时播放 开始前缀&#xff08;00000001或000001&#xff09;&#xff0b;NALU数据  绝大部分编码器的默认输出格式   一共有两种起始码start_code    ①3字节0x000001  单帧多s…

C++面向对象特性——多态

C面向对象之多态什么是多态&#xff1f;为什么使用多态&#xff1f;虚函数的定义虚函数的实现机制哪些函数不能被设置为虚函数&#xff1f;虚函数的访问指针访问引用访问对象访问成员函数中的访问构造函数和析构函数中访问纯虚函数抽象类虚析构函数重载、隐藏、覆盖菱形继承虚拟…