前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >React进阶(1)-理解Redux

React进阶(1)-理解Redux

原创
作者头像
itclanCoder
修改2020-10-20 10:56:07
1.4K0
修改2020-10-20 10:56:07
举报
文章被收录于专栏:itclanCoderitclanCoderitclanCoder

前言

在React中,数据流是单向的,并且是不可逆的,这其实,也很好理解,之所以这么设计,是因为组件复用的特点

父(外部)组件向子(内部)组件传递数据是通过自定义属性props值的方式进行实现的,并且在子组件内部通过this.props进行获取,它并不能直接被修改,如果想要修改,那么得通过React内置的一个setState的方法进行触发

而子组件想要传递数据给父组件,是通过调用父组件的方法进行通信

一个组件可能存在着很多状态,组件之间有时需要进行通信,对于多个组件状态维护,如果依旧用原来的方式,那么就比较复杂了的

那么Redux正好解决了这一问题.个人觉得,Redux学起来很抽象,的确是块硬骨头,但是高山始终是要越过的

下面就一起来学习下Redux的

您将在本文中学习到

  • Redux是什么
  • Redux的使用场景以及与不使用Redux的灵魂对比
  • Redux的工作流程
  • Redux的设计基本原则

本篇虽不涉及代码层面上的,但是对后续编码Redux非常重要,磨刀不误砍柴工

Redux是什么?

官方解释:JavaScript应用程序的可预测的状态容器(一个管理应用程序状态的框架)

通俗一点:管理组件公共数据状态的容器(仓库/区域)

解决的问题: 当应用组件拥有多个状态,并且组件之间需要共享数据状态时,从原始的组件传递数据的方式中解脱出来,集中管理组件的状态

你可以把Redux理解为一个仓库,房产中介.拥有很多共享的房源的一个管理者,后面会有具体的例子

Redux的使用场景

从上面提到的Redux解决问题可以看出,Redux只是用来管理和维护组件的状态的

React开发的模式就是组件化开发,将一个大的应用拆分成若干个小的应用,然后拼接成一个大的应用,而编写一个大小应用就是在编写各个大小组件

而组件的显示形态又取决于它的状态,这不区分于无论是外部的props还是内部的state,而组件之间有时需要共享传递数据,Redux仅仅就是用来管理这些组件的状态的

在一些开发者眼里,项目里要是没有用到Redux,就觉得很low,要么把Redux捧得高高在上,要么说都已经快0202年了,都用React hook了,鄙视得不行,个人觉得完全没有必要.

React与Redux本身就是解决两个不同方向的问题,某种程度上讲,React可以视为MVC架构中的视图层V,而Redux则是model数据层M,而C层往往是连接视图层和model的连接器,往往处理前端数据请求,路由跳转等业务逻辑

即使不用Redux,照样能做小应用,只是略复杂繁琐一些而已,下面会介绍他们之间的对比

那么对于技术选型,什么时候用Redux什么时候不用?

以下是选用Redux的场景:

  • 项目非常庞大,公共组件与业务组件非常多,用户的使用方式比较复杂
  • 不同身份的用户角色权限管理(例如很多后台管理系统,普通用户,超级管理员,VIP用户)读,写权限管理等
  • 多个用户之间可以协作实时操作(很多那种在线敏捷协作办公文档工具,多个用户可以实时编辑操作同一份文档等的,例如石墨文档,语雀,confluence.钉钉等的)
  • 需要与服务器大量的交互,或者使用了webscoket的,聊天,直播等应用的
  • 视图层view需要从多个来源获取数据
  • ....只要你发现React解决不了的问题,遇到多交互,多数据源的,那么就可以考虑使用Redux的

反之,则以下则是没有必要使用Redux

  • UI层非常简单,只是用于渲染,无复杂的数据交互,依赖外部的props就可以渲染组件
  • 用户的使用方式比较简单,页面之间比较独立,没有互相协作
  • 与服务器之间没有大量交互

当你发现使用React实在解决不了的问题,在各个组件之间传递数据非常复杂,很痛苦时,那么就可以考虑使用Redux了的,只要你hold住,没有所谓的高大上技术,只有适合自己业务的技术

盲目引入Redux只会增加项目的复杂度,引入新的技术应该是循序渐进的

 不使用Redux与使用Redux的灵魂对比

 不使用Redux与使用Redux的灵魂对比

下面这张组件树状态图的对比就很好的解释了使用Redux与不使用Redux的区别

一个React应用(例如:pc网站,手机app应用,后台管理系统等用React技术栈构建的应用)其实就是一颗由组件构成的树,如上图所示,在这颗树的根结点,最顶层的组件就是该应用的本身,由于组件都是以树结构组织起来的,当每个组件被渲染时,它都会递归地渲染下级组件

(根节点就是最顶层的组件,该应用本身)

假设红色圆圈代表的是一个应用的子组件,如果想要把该红色圆圈组件的状态数据传递给父级或者非父级组件,它是通过调用父组件的方法来实现,这样一层一层往上传,如果组件树很庞大的话,那么就会变得非常繁琐

在小型项目中,Redux并不是必需的,但是使用Redux却是一劳永逸的,管理组件的状态方便得多,对于大型应用来说,单纯使用原始的数据传递方式

那么组件之间的传值会变得非常复杂,如果要做一个大型的应用,那么就需要在React的基础上配置一个数据层的框架进行结合的使用

如果改为右边的Redux处理方式,将红色圆圈组件的状态数据放到一个Store仓库当中集中进行管理,哪个组件需要的话,直接派发给哪个组件就可以了的.

在Redux中,要求把组件的数据放到公共的存储仓库(区域)当中,让组件尽可能的减少状态数据存储,换而言之,所有组件自身内部状态数据都不放在state里面了,把它放到Store这样的一个存储仓库当中去

其实本质上来说,是放到reducer里面去管理,Store从Reducer中拿到返回的数据state,最后供外部组件的取用

当红色圆圈组件想要改变数据传递给其他组件时,只需要去改变Store里面的存储红色圆圈组件的数据就可以了

一旦Store公共存储的状态数据发生改变了的,由于其他组件是公用Store的数据,那么其他组件就会感知到Store的数据发生了改变,从而自身组件也会跟着改变

只要Store公共存储区域的数据发生改变,凡是共用了Store里面的数据的组件都会重新的取数据

这样一来,红色圆圈组件的数据就非常容易的传递给其他组件了,无论是它的父级组件还是兄弟,非兄弟组件的

Redux就是把组件的数据放到一个公共的区域(仓库)中进行存储,当改变Store存储区域里面的数据时,其他组件如果用到了公共区域的数据,那么就会感知到数据的变化,它会自动的更新取Store中最新的数据

这样话,无论你的应用组件嵌套得有多么复杂,多么深,走的流程都是一样的,组件之间并不会干扰,低耦合的效果

当组件一修改,把修改的数据放到Store当中,而其他组件又从Store当中再来取,这样的话,组件与组件之间并不是直接进行通信的,是通过这么一个store中间角色来实现数据的传递共享的.

这样的话,组件的数据传递就简单多了的,也避免了组件与组件之间频繁通信,容易产生混乱的问题

Redux其实是Flux数据框架的一个替代演进,同样强调的是单向的数据源,保持状态只具备读的能力,而数据改变只能通过纯函数完成基本,这和原先中React的UI=render(data)完全吻合.

React与Redux是两个独立的框架,前者是用于组件视图层的渲染,而后者是管理组件的数据

 Redux的工作流程

现在已经知道了使用Redux与不使用Redux的区别,那么现在是时候来了解一下Redux的工作流程了,下面这个流程图对于理解Redux很重要 先附上一张Redux工作流的流程图:以后会在代码中逐步的体现

上面的Redux工作流图中,以中间为准:包括了Store,ReactComponents,Actions Creators,以及Reducers

其中Store代表的就是负责组件存储所有公共状态的数据,全局只有一个Store.(这里你可以把它理解为类似生活当中中介公司管理房源的仓库(数据库)的区域经理)

实质上:store就是把Reducer关联到一起的一个对象,它提供dispatch(action)方法更新state,以及getState方法获取state

React Components:指的是页面上的任意一个组件(你可以理解为小区公寓楼里的每个房间,而你就是住在里面的租房用户)

Actions Creators:具体要干什么事情,触发的动作,可以看做一个交互动作,改变应用状态或view的更新,都需要通过触发action来实现,Action的执行结果就是调用Dispatch来处理相应的事情,实现页面视图view的更新,唯一的办法就是调用dispatch派发action

它是一个javascript对象,是用来描述事件的行为的,对象里记录了相关的信息,例如:todolist的添加,删除list的这个具体操作,就是一个action

(当你想要提出换房的时候,跟中介公司管理房源的经理说,你要换个带有沙发,电视,配备厨房的两室一厅的房子,因为增加人口了,现有的房子住不下了的,你要做的什么事情,提出的条件信息就是数据),这个动作可以理解为actions creators

在你提出换房的时候,房产中介公司经理虽然手握很多房源,但是他也没有办法记得所有的房子相关信息,它需要去数据库(仓库)里去查,你常常看到中介小哥带你看房的时候

手上拿一个单子,Excel表格跟你介绍房源的时候,你可以把这个单子,Excel表格理解为一个实时记录本,只要有房子出租去了,这个表格就会实时更新(新旧信息的核实对比),返回一张新的房源信息表单给房产中介的经理

Reducer:可以把上面的用于实时更新记录房源信息的记录本称为Reducer,它只用作于根据旧的房源与提出新的需求(动作),总是会返回一张新的记录本给房产中介经理

实质上:Reducer是根据action发出的type(动作类型)来做什么事(返回最新的state给store等逻辑操作)

现在归纳一下整个流程: 

我(租客/组件React Component)想要换一个xxx信息的房子(Actions creators,具体要做的什么事情),房产中介经理收到了请求,他得根据你提供的一些需求信息去找相应的房源信息

但是房源太多,需要借助一个实时的记录本去查看符合条件的房源信息,当查到符合条件的信息后,这个记录本(Reducer)把最新的信息会返回给房产经理(Store),最终把信息返回给用户React Components,实现房子替换的更新

虽然文字啰嗦了点:但是Redux就是这么一回事,我要换大房子,房产中介经理听到后,它去记录本里面去查,查到之后,返回到房产中介经理,然后最终在返回给我,实现房子的替换

那么转换为代码理解: 

页面上的一个组件,想要获取更新Store中的数据,跟Store说,我点击这个按钮,要更新这个组件的数据,要干什么事情,做的这个具体动作就是Actions Creators,这时会派发(dispatch) 该动作(action)给Store,Store会去Reducer里面去查一下,Reducer会返回一个新的结果给Store,Store拿到最新的数据结果后,返回给页面上的组件,实现页面组件的更新

大家可以先仔细体会上面这段文字的含义,在后续的实例代码中,在回过头来对比着代码与文字进行理解的,后续还会在拿出来的

 Redux的设计基本原则

在Redux中有以下几个设计基本原则

  • 单向数据流
  • 唯一数据源
  • 保持状态只读
  • 数据的改变只能通过纯函数reducer来完成

单向数据流: 这个其实与props不能直接被修改一样,在父组件向子组件传递数据时是通过属性的方式进行传递的,而子组件内部通过this.props进行接收,但是外部传递过来的props属性不能直接被修改,若想要修改,需要借助React内置的setState方法进行触发

唯一数据源: 它指的是组件的应用状态数据应该只存在唯一的Store上,这一点是不同于Flux的,在Flux中允许有多个store。而在Redux中整个组件的应用只保持一个Store,所有组件的数据源就是这个Store上的状态,可以将它Store理解为一个全局的变量对象

保持状态state可读: 不能直接的去修改状态,要修改Store的状态,必须要通过派发(dispatch)一个action对象去完成

然后组件渲染的对应的界面要更改的话,实际更改的就是组件的状态,如果状态都是只能读不能修改的话,那么界面就不会更新变化了

想要更改用户界面的渲染,就要改变组件的应用状态,但时改变组件状态的方法不是直接去修改状态上的值,而是创建一个新的状态对象返回给Redux,由Redux完成新的状态的组装

组件数据的改变只能通过纯函数完成

所谓的纯函数,就是指Reducer,而Redux某种程度上讲,它是Reducer+Flux的组合,其中这Redux的Red代表就是Reducer,而ux就是Flux,但是又不同于Flux,它更像是Flux的一个实现,演进。它是为了描述Action如何改变组件的状态的

这也是为什么Redux这个名称比较抽象的原因,其中Reducer类似一个数组中的迭代器函数reduce

var arr = [1,2,3,4,5,6]
var sum = arr.reduce(function reducer(prevValue, currentValue,index,array){
    console.log(`上一次调用回调返回的值(或者是提供的初始值): ${prevValue},数组中当前被处理的元素: ${currentValue}, 当前元素在数组中的索引: ${index}, 调用的数组: ${array}`);
  return prevValue+currentValue;
},0)
console.log(sum); // 21

VM1742:3 上一次调用回调返回的值(或者是提供的初始值): 0,数组中当前被处理的元素: 1, 当前元素在数组中的索引: 0, 调用的数组: 1,2,3,4,5,6
VM1742:3 上一次调用回调返回的值(或者是提供的初始值): 1,数组中当前被处理的元素: 2, 当前元素在数组中的索引: 1, 调用的数组: 1,2,3,4,5,6
VM1742:3 上一次调用回调返回的值(或者是提供的初始值): 3,数组中当前被处理的元素: 3, 当前元素在数组中的索引: 2, 调用的数组: 1,2,3,4,5,6
VM1742:3 上一次调用回调返回的值(或者是提供的初始值): 6,数组中当前被处理的元素: 4, 当前元素在数组中的索引: 3, 调用的数组: 1,2,3,4,5,6
VM1742:3 上一次调用回调返回的值(或者是提供的初始值): 10,数组中当前被处理的元素: 5, 当前元素在数组中的索引: 4, 调用的数组: 1,2,3,4,5,6
VM1742:3 上一次调用回调返回的值(或者是提供的初始值): 15,数组中当前被处理的元素: 6, 当前元素在数组中的索引: 5, 调用的数组: 1,2,3,4,5,6
VM1742:6 21

上面的代码中是做一个简单的累加,reducer函数接收四个参数,第一个参数是上一次调用返回的结果,第二个参数是当前被处理的元素的值,第三个是当前元素在数组中的索引,第四个是调用的原数组

这个reduce的方法接收一个函数作为累加器,reduce 为数组中的每一个元素依次执行回调函数

而在Redux中,每个reducer纯函数如下所示

reducer(state, action)

其中reducer函数的第一个参数state是指当前的状态值,而第二个参数action是接收到的action对象

reducer函数要做的事情就是根据state和action的值产生一个新的对象返回给Store,它是定义整个组件应用状态如何更改,根据Action动作行为去更新Store中的状态

注意的是reducer必须是纯函数,换句话说,reducer函数的返回结果必须完全由参数state和action决定,而且不产生任何的副作用,也不能修改参数state和action对象

如下一个典型的reducer示例,reducer只是一个函数名称,你是可以任意取的,如下一个计数的counter纯函数

function counter(state = 0, action) {
  switch (action.type) {
  case 'INCREMENT':
    return state + 1;
  case 'DECREMENT':
    return state - 1;
  default:
    return state;
  }
}

从上面的例子看得出,reducer函数不光接受action为参数,还接受state参数,也就是说,Redux中的reduce函数只负责计算组件的状态,却不负责存储组件的状态

在Reducer函数中往往包含action.type为判断条件的if-else或者switch语句,根据action,总是返回一个新的状态,这个新的状态的结果返回给store,store就会将原来上一次的state进行替换更新,最终达到改变state这么一个过程

结语

本节主要介绍了Redux,它与React是两个独立的产品,两个框架做的事情的方向不一样,React是用作于视图层的渲染,也相当于MVC中的V层,而Redux它是用于管理组件公共数据的Model层,更近一步讲,它是Reducer与Flux的一种结合,改进.

对比了使用Redux与不使用Redux的区别,以及Redux的工作流,最后Redux的设计基本原则,其中前两个,个人觉得对于理解Redux是非常重要的

当然现在也可以使用高阶组件,React hooks的写法,可以不用Redux了的,也有类似于dva这样的框架,基于Redux以及中间件(Redux-saga)的数据流方案

但是Redux依然是主流,只要你能够应付项目中开发需求,哪个用得爽就用哪个的,Redux虽然确实是绕了一些,有时候在各个文件之间进行来回切换,对于模块化的拆分,如果不是很清楚Redux的使用流程,无论是后续维护还是迭代升级,都挺痛苦的

本篇并不是什么高大上的内容,比较浅显,概念性的文字比较多,后续会结合具体的代码进一步理解Redux

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
    •  不使用Redux与使用Redux的灵魂对比
    相关产品与服务
    容器服务
    腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档