原文链接:https://blog.csdn.net/hl582567508/article/details/76982756

redux中文文档:http://cn.redux.js.org/

React项目开发中的数据管理

  对于React的初学者在项目开发中常常还是会以DOM操作的思维方式去尝试获取、修改和传递数据,但是这种思想,在React思想中显然是错误的,针对这种情况下文将进行一个简易的总结。我们将从基础的纯React组件之间的传值开始论述,然后分析React结合Redux之间的数据传递,以及最后基于dva脚手架的数据传输问题进行探讨和分析。

一、 原生React组件的数据传输管理:

  原生React组件之间的数据传输主要依赖于两个关键词:属性(props) 和状态(state)。每一个组件都是一个对象,props是对象的一个属性,组件对象可以通过props进行传递。React 的核心思想是组件化的思想,应用由组件搭建而成,而组件中最重要的概念是State(状态),State是一个组件的UI数据模型,是组件渲染时的数据依据。state与props的最大区别在于props是不可变的而state是可变的。具体内容后面会详细讲解。

原生React组件之间数据传递场景可以分为以下四种: 
- 组件内部的数据传输 
- “父组件”向“子组件”传值 
- “子组件”向 “父组件”传值 
- “兄弟组件”之间的传值

  1. 组件内部的数据传输

  在初学过程的项目开发中常常会有去尝试DOM操作的冲动,虽然大部分情况下这种尝试是错误的,但是在某些时候还是不得不需要获取对DOM的值进行操作。例如:点击一个按钮之后触发一个点击事件,让一个input文本框获得焦点。jQuery开发者的第一反应肯定是给button绑定点击事件,然后在事件中通过$(‘select’)获取到要操作的节点,再给节点添加焦点。然而在React中这种操作是不允许的,而React中应该怎么做呢?

React Refs属性:

import React, { Component } from 'react';class MyComponent extends Component({ handleClick = () => { // 使用原生的 DOM API 获取焦点 this.refs.myInput.focus(); }, render: function() { // 当组件插入到 DOM 后,ref 属性添加一个组件的引用于到 this.refs return ( <div> <input type="text" ref="myInput" /> <input type="button" value="点我输入框获取焦点" onClick={this.handleClick} /> </div> ); } }); ReactDOM.render( <MyComponent />, document.getElementById('example') ); 

  我们可以从上面的代码当中看到 其中的ref在功能上扮演起了一个标识符(id)的角色,this.refs.myInput.focus()也有一种document.getElementById(‘myInput’).focus()的味道。

  上面的操作我们也称为React表单事件,React表单事件中除了ref具有关键作用外,还有另一个关键参数’event’。例如:当我需要实时获取到一个文本框里面的内容,然后进行一个判断,当满足某个条件的时候触发另一个事件。这个时候就需要使用到这个一个关键参数’event’。

React 表单事件-event参数:


class MyComponent extends Component{ handleChange = (event) => { if(event.target.value === 'show'){ console.log(this.refs.showText); } }; render(){ return( <div> <input type="text" onChange={this.handleChange}/> <p ref='showText'>条件满足我就会显示在控制台</p> </div> ) } } export default MyComponent; 

  上面实例实现的效果就是,通过event.target.value获取当前input中的内容,当input中输入的内容是show的时候,控制台就将ref为showText的整个节点内容打印出来。从这个实例当中我们也看到了,event作为一个默认参数将对应的节点内容进行了读取。

  因此在组件内部涉及的DOM操作数据传递主要就是这两种方式,可以根据不同的场景选择不同的方式。虽然ref适用于所有组件元素,但是ref在正常的情况下都不推荐使用,后面会进行介绍通过 state管理组件状态,避免进行DOM的直接操作。

  1. “父组件”向“子组件”传值

  父组件与子组件之间的通信通常使用props进行。具体如下:


import React,{ Component } from 'react'class ChildComponent extends Component{ render (){ return ( <div> <h1>{this.props.title}</h1> <span>{this.props.content}</span> </div> ) } } class ParentComponent extends Component { render (){ return ( <div> <ChildComponent title="父组件与子组件的数据传输测试" content="我是传送给子组件span中显示的数据" /> <p>我是父组件的内容</p> </div> ) } } export default ParentComponent; 

上面示例展示了父组件向子组件传递了两个props属性分别为title和content,子组件通过this.props获取到对应的两个属性,并将其展示出来,这个过程就是一个父与子组件之间的数据交互方式。但是也可以从例子中看到props的值是不变的,父传给子什么样的props内容就只能接收什么样的使用,不能够在子中进行重新赋值。

  1. “子组件”向 “父组件”传值

  本例中将会引入了管理组件状态的state,并进行初始化。具体如下:

import React, { Component } from 'react';
//子组件
class Child extends Component { render(){ return ( <div> 请输入邮箱:<input onChange={this.props.handleEmail}/> </div> ) } } //父组件,此处通过event.target.value获取子组件的值 class Parent extends Component{ constructor(props){ super(props); this.state = { email:'' } } handleEmail = (event) => { this.setState({email: event.target.value}); }; render(){ return ( <div> <div>用户邮箱:{this.state.email}</div> <Child name="email" handleEmail={this.handleEmail}/> </div> ) } } export default Parent;

通过上面的例子可以看出”子组件”传递给”父组件”数据其实也很简单,概括起来就是:react中state改变了,组件才会update。父写好state和处理该state的函数,同时将函数名通过props属性值的形式传入子,子调用父的函数,同时引起state变化。子组件要写在父组件之前。

从本示例中也可以看出state可以通过setState进行重新赋值,因此state是可变的,表示的是某一时间点的组件状态。

  1. “兄弟组件”之间的传值

  当两个组件不是父子关系,但有相同的父组件时,将这两个组件称为兄弟组件。严格来说实际上React是不能进行兄弟间的数据直接绑定的,因为React的数据绑定是单向的,所以才能使得React的状态处于一个可控的范围。对于特殊的应用场景中,可以将数据挂载在父组件中,由两个组件共享:如果组件需要数据渲染,则由父组件通过props传递给该组件;如果组件需要改变数据,则父组件传递一个改变数据的回调函数给该组件,并在对应事件中调用。从而实现兄弟组件之间的数据传递。

import React, { Component } from 'react';
//子组件
class Child extends Component { render(){ return ( <div> 我是子组件邮箱:<input onChange={this.props.handleEmail} defaultValue={this.props.value} /> </div> ) } } //兄弟组件 class ChildBrother extends Component { render(){ return ( <div> 我是兄弟组件:{this.props.value} </div> ) } } //父组件,此处通过event.target.value获取子组件的值 class Parent extends Component{ constructor(props){ super(props); this.state = { email:'' } } handleEmail = (event) => { this.setState({email: event.target.value}); }; render(){ return ( <div> <div>我是父组件邮箱:{this.state.email}</div> <Child handleEmail={this.handleEmail} value={this.state.email}/> <ChildBrother value={this.state.email}/> </div> ) } } export default Parent;

上面例子中就是child组件的值改变后存储在父组件的state中,然后再通过props传递给兄弟组件childBrother。从而实现兄弟组件之间的数据传递。

二、 基于Redux的React项目开发中的数据管理

  前面在分析原生React组件之间的数据传输中讲到两个关键词:state和props,在项目的实际开发过程中,这里的state可能包括服务器响应数据、缓存数据、本地生成尚未持久化到服务器的数据,也包括 UI 状态,如激活的路由,被选中的标签,是否显示加载动效或者分页器等等。

  管理不断变化的 state 非常困难。如果一个 model 的变化会引起另一个 model 变化,那么当 view 变化时,就可能引起对应 model 以及另一个 model 的变化,依次地,可能会引起另一个 view 的变化。直至你搞不清楚到底发生了什么。state 在什么时候,由于什么原因,如何变化已然不受控制。

因此在这些问题下便产生了 Redux ,在Redux的理念中通过限制更新发生的事件和方式试图让state的变化变的可预测。Redux可以用三个基本原则来描述:

  • 单一数据源:整个应用的 state 被储存在一棵 object tree 中,并且这个 object tree 只存在于唯一一个 store 中。
  • State是只读的:唯一改变 state 的方法就是触发 action,action 是一个用于描述已发生事件的普通对象。
  • 使用纯函数来执行修改:为了描述 action 如何改变 state tree ,你需要编写 reducers。

redux的基本工作流程为store进行管理state和reducers,reducers接收一个action和原始的state,生成一个新的state,dispatch进行触发一个action,打一个比方:store就好比是一个银行,state就是银行中存的钱,reducers就是银行的用户管理系统,dispatch就是取款机,action就是取款机发出的请求,component就是用户。所以当我们要完成一个取钱的过程,首先就是用户(component)通过取款机(dispatch)发起一个(action)取款的请求,当银行的用户管理系统(reducers)接收到请求以后,调取用户的原来的账户信息(old state),进行相应(action)操作,如果没有什么问题则更改账户信息生成新的账户资料(new state),并把钱取给用户(返回给component)。

整个流程可以通过下图表示:

我们可以来看一个简单的例子:基本功能就是在一个任务管理器中添加新的任务,我们主要看其数据走向。

Action:

let nextTodoId = 0
export const addTodo = (text) => ({ type: 'ADD_TODO', id: nextTodoId++, text })

Reducers:

const todos = (state = [], action) => {switch (action.type) { case 'ADD_TODO': return [ ...state, { id: action.id, text: action.text, completed: false } ] default: return state } } export default todos 

Component:

import React from 'react'
import { connect } from 'react-redux'
import { addTodo } from '../actions' let AddTodo = ({ dispatch }) => { let input return ( <div> <form onSubmit={e => { e.preventDefault() if (!input.value.trim()) { return } dispatch(addTodo(input.value)) input.value = '' }}> <input ref={node => { input = node }} /> <button type="submit"> Add Todo </button> </form> </div> ) } AddTodo = connect()(AddTodo) export default AddTodo 

Store:

import React from 'react'
import { render } from 'react-dom'
import { createStore } from 'redux' import { Provider } from 'react-redux' import App from './components/App' import reducer from './reducers' const store = createStore(reducer) render( <Provider store={store}> <App /> </Provider>, document.getElementById('root') ) 

上面示例就是一个简易的react-redux项目中的数据请求与处理,component发起dispatch(addTodo(input.value))请求,reducers接收’ADD_TODO’返回一个新的state,store进行管理整个reducers和state将其结果渲染在页面当中。

总结:

  redux只是对于react的state进行了管理,对于react的props并没有进行管理,这也与props本身的特性有关,props本身就是只读属性,所以可控性比较强,不需要进行再次包装管理。前面讲的主要是针对于同步情况下的redux的请求与处理过程,并没有阐述异步情况,由于其基本思想是一样的只是异步请求需要使用redux-saga的fetch请求远程服务器,然后再接收收据后进行相应的操作。具体的流程会在后面的基于dva的React项目开发中的数据管理中进行讲解。

三、 基于dva的React项目开发中的数据管理

  dva是基于 redux、redux-saga 和 react-router@2.x 的轻量级前端框架。是使用React技术栈进行前端开发的脚手架。

dva实际上并没有引入什么新的概念,依旧使用的是React、Redux、React-route技术栈的相关概念,唯一的特点就是简化了React和Redux、Redux-saga之间的数据数据交互。可以从下面的实例中来进行简要了解:

models:

import { getInputOutputProfiles,deleteInputOutputProfiles,addInputOutputProfiles } from "../../services/InputOutputManagement"export default {namespace : 'input_output',state : {...data:[],...},effects : {*getInputOutputProfiles({ payload }, { put, call, select }) {const type_input='REMOTEFILESHARED_INPUT'; const type_output='REMOTEFILESHARED_OUTPUT'; const token = yield select(state => state.home.token); const result_input = yield call(getInputOutputProfiles,{payload:{token,type:type_input}}); const result_output = yield call(getInputOutputProfiles,{payload:{token,type:type_output}}); let data=[]; result_input.remoteFileList.map(value => { data.unshift(value) }); result_output.remoteFileList.map(value => { data.unshift(value) }); console.log(data); yield put({type:'setData',payload:{ data: data }}); }, ... } }, reducers : { ... setData(state,{ payload:{data} }){ return { ...state, data:data } }, ... }, subscriptions : { setup({dispatch, history}) { return history.listen(({pathname}) => { if (pathname === '/system/input_output') { dispatch({ type:'getInputOutputProfiles' }); } }); } } }

component:

class SectionPanel extends Component{ ... handleSubmit = () => { this.props.dispatch({ type: 'input_output/addInputOutputProfiles', payload: { id:this.props.id, type:this.props.type }}); }; ... } const mapStateToProps = ( state ) => { return state; }; export default connect(mapStateToProps)(SectionPanel); 

 从这个示例当中我们可以看到,model中进行state的定义,以及reducers的定义,在dva中reducers是唯一可以改变state的地方。从例子中我们可以看到,在subscriptions中进行了一个订阅监听,当加载pathname === ‘/system/input_output’的时候通过dispatch发起一个异步请求getInputOutputProfiles,请求会连接到服务器,从服务器端获取相应的数据,然后再对数据进行处理,再执行reducers中的同步setData:yield put({type:’setData’,payload:{ data: _temp }});改变当前state中的data数据。有了data数据,组件就可以遍历数据呈现给用户。

 这是一个由订阅数据源而发起的一个改变state的方式,除此之外,state改变和去向主要应用在组件当中,如上component当中所示,组件中需要使用state,首先要进行state和props的映射,然后组件就可以通过this.props进行获取相应的state值,因此通过mapStateToProps方法进行映射,然后通过connect方法将映射的结果与组件绑定,此处需要知道的是组件中发起请求的dispatch也是需要将组件与redux连接(connect)之后才能在组件中使用dispatch。这些准备工作做好之后便可以在组件中发起dispatch请求改变state状态了。

 从上面的示例中我们会发现在dva中不需要显式的编写action,也不用写创建store的过程,而是在dispatch中将传递action名改变为对象,对象包含两个部分{ type:”,payload:{ } },具体触发reducers的过程以及生成新的state的具体操作都是由dva内部进行,从而简化了操作。

以上便是一个dva项目的数据传递流,下面我以图的形式进行展示:

总结

 从原生React到react-redux再到dva其思想上实际并没有本质上的颠覆,redux简化react的数据管理,dva简化react-redux项目的数据管理,dva最终的目的其实也只有一个,就是写更少的代码做更多的事情。

转载于:https://www.cnblogs.com/zyx-blog/p/9294991.html

React项目开发中的数据管理相关推荐

  1. react项目开发中出现浏览器翻译功能造成的bug

    最近使用react开发一个项目时,测试提出一个bug,说是在某些浏览器上面本来是序号''5''变成了大写的''五'' 自己却一直无法重现,最后发现是测试人员浏览器打开了翻译功能 解决方案: 解决思路是 ...

  2. react项目开发步骤_成为专业React开发人员的31个步骤

    react项目开发步骤 我为达到可雇用水平而进行的每个项目和课程. (Every single project and course I took to reach a hireable level. ...

  3. 项目开发中常用JS表单取值方法

    项目开发中常用JS表单取值方法 一.常用表单基本取值方法(form1为表单名称,TextBox1为控件ID,以文本框为例,html控件与web服务器控件是一样的)         1.form1.Te ...

  4. aspnet是前端还是后端_项目开发中无法回避的问题:前端和后端如何合作和并行工作?...

    项目开发中无法回避的问题:前端和后端如何合作和并行工作? 前端,后端要想非常愉快的合作开发和集成,那最开始第1步就是要定义一套共用的数据模型接口.这一步是所有工作可以顺利进行的前提. 数据模型接口之后 ...

  5. ssh项目同时使用mysql跟sqlserver数据库_MSSQL_如何把sqlserver数据迁移到mysql数据库及需要注意事项,在项目开发中,有时由于项目 - phpStudy...

    如何把sqlserver数据迁移到mysql数据库及需要注意事项 在项目开发中,有时由于项目开始时候使用的数据库是SQL Server,后来把存储的数据库调整为MySQL,所以需要把SQL Serve ...

  6. IntelliJ IDEA 项目开发中各个目录的关系

    项目根目录(Project Root):本地硬盘中存放项目内容的根目录 项目名称(Project Name):IDEA 保存的项目名称,默认和项目根目录的名称相同,可以修改.有啥作用?通过 IDEA ...

  7. 项目开发中发布更新文档备注

    项目开发中 经常会遇到 某一个团队成员更新了,配置文件但是没有及时沟通导致项目发布后出现异常的情况. 这时就需要一个项目更新发布的规范说明文件,比如每次更新更改了哪些,进行文档备注. 更新的脚本文件也 ...

  8. Vue项目开发中的点滴积累系列文章

    题记 -- 严于律己,精于行动,点滴积累,着眼未来,你也许不负青春 Vue.js(读音 /vjuː/, 类似于 view) 是一套构建用户界面的渐进式框架 本文章 记录从基础入门到实际项目开发中的点点 ...

  9. 项目开发中对使用的第三方库统一进行管理__添加属性表/页

    最近接手的一个项目开发中用到了很多第三方库,比如boost.gdal.xerces等等这些.从接手项目到现在从中学习到了很多之前从未见识过的东西.项目绝大部分都是前辈们写的,对于里面的对第三方库的管理 ...

最新文章

  1. easyexcel生成excel_阿里JAVA解析Excel工具easyexcel
  2. list,set,map,数组间的相互转换
  3. 拿到offer可以不去吗_2020应届毕业生求职难吗?不难!教你四招,拿到心动的Offer...
  4. 【数理知识】特征值、特征向量、左特征向量
  5. “PC一哥”联想的这些年
  6. 网页侧边浮动条的实现
  7. hadoop2.0以后不需要借助cywin运行在Windows上
  8. docker 配置nginx镜像出现 403 Forbidden的问题
  9. 科学技术究竟有没有国界?独家专访 IEEE 高级会员张海霞教授
  10. Android性能优化系列---管理你的app内存(一)
  11. 基于 AUTOSAR 的电动汽车中央控制单元 CAN 通信软件开发
  12. Day001-2021-07-29 变量定义/数据类型/基础运算 判断/循环/数组
  13. 小白带你学安卓——初识android
  14. 傻瓜攻略(六)——MATLAB实现RBF神经网络
  15. HaLoop——适用于迭代计算的Hadoop
  16. 微信小程序本地图片处理--按屏幕尺寸插入图片
  17. fluent物性参数拟合多项式,python,matlab多项式图像绘制
  18. 产品运营数据分析—SPSS数据分组案例
  19. dnf最新地图编号2020_DNF100级即将迎来新城镇 地图小贴士
  20. 双离合档把上按钮作用_求双离合档位详细解释

热门文章

  1. linux覆盖文件如何还原_大数据笔试真题集锦-——第十九章Linux面试题
  2. Maven的安装与Eclipse的配置
  3. .html .php .asp,html静态页面中执行php、asp函数代码
  4. java接口有非抽象方法_[Java教程]纳尼,java可以在接口中实现非抽象方法了?
  5. 【实验】配置DHCP和NAT访问Internet公网案例
  6. 2019 年 CNCF 中国云原生调查报告
  7. 一文读懂分布式架构知识体系(内含超全核心知识大图)
  8. mysql lock scope_scope_lock模式详解
  9. 本地服务器模板网站怎么安装,本地服务器怎样安装帝国CMS模版网站
  10. 计算机学校推荐专科,专科计算机专业学校推荐