React | Redux的使用详解

news2025/2/22 0:30:34

✨ 个人主页:CoderHing

🖥️ React.js专栏:React.js  Redux的使用详解
🙋‍♂️ 个人简介:一个不甘平庸的平凡人🍬

💫 系列专栏:吊打面试官系列  16天学会Vue  7天学会微信小程序  Node专栏

🍀 格言: ☀️ 路漫漫其修远兮,吾将上下而求索☀️

👉 你的一键三连是我更新的最大动力!❤️

❤️ 本文约 6000 字,预计阅读需要 30 分钟 ❤️


目录

一、Redux的核心思想

理解JavaScript纯函数

副作用概念的理解

纯函数的案例

纯函数的作用和优势

二、Redux的基本使用

为什么需要redux

Redux的核心理念 - Store

Redux的核心理念 - action

Redux的核心理念 - reducer

Redux的三大原则

Redux测试项目搭建

Redux的使用过程

Redux结构划分

Redux使用流程

Redux官方图

三、React结合Redux

redux融入react代码

react-redux使用

react-redux源码导读

四、Redux的异步操作

组件中异步操作

redux中异步操作

理解中间件

如何使用redux-thunk

五、redux-devtool

redux-devtools

六、reducer的模块拆分

Reducer代码拆分

Reducer文件拆分

combineReducers函数


一、Redux的核心思想

理解JavaScript纯函数

  • 函数式编程中有一个非常重要的概念叫纯函数 JavaScript 符合函数式编程的范式 所以也有纯函数的概念
    • 在react开发中纯函数是被多次提及的
    • react中组件就被要求像是一个纯函数(为什么是像?因为还有class组件), redux中有一个reducer的概念,也要求必须是一个纯函数
    • 掌握纯函数对于理解很多框架的设计是非常有帮助的
  • 纯函数的维基百科定义:
    • 在程序设计中 若一个函数 符合以下条件 那么这个函数被称为纯函数
    • 此函数 在相同的输入值时 产生相同的输出
    • 函数的 输出和输入值以外的其他隐藏信息或状态无关 也和 I/O 设备产生的外部输出无关
    • 该函数 不能有语义上可观察的函数副作用 诸如 “触发事件” 使输出设备输出 或更改输出值以外物件的内容
  • 总结几句话:
    • 确定的输入 一定会产生确定的输出
    • 函数在执行过程中 不能产生副作用

副作用概念的理解

  • 那么这里又有一个概念,叫做副作用,什么又是副作用呢?
    • 副作用(side effect)其实本身是医学的一个概念,比如我们经常说吃什么药本来是为了治病,可能会产生一些其他的副作用
    • 在计算机中,也引用了副作用的概念,表示 在执行一个函数时,除了 返回函数值之外,还对 调用函数产生了附加的影响,比如 修改了全局变量,修改参数或者改变外部的存储
  • 纯函数在执行的过程中就是不能产生这样的副作用:
    • 副作用往往是产生 bug的 “温床”

纯函数的案例

  • 我们来看一个对数组操作的两个函数:
    • slice :slice截取数组时不会对原数组进行任何操作,而是生成一个新的数组
    • splice :splice截取数组,会返回一个新的数组,也会对原数组进行修改
  • slice就是一个纯函数,不会修改数组本身,而splice函数不是一个纯函数

纯函数的作用和优势

  • 为什么纯函数在函数式编程中非常重要呢?
    • 因为你可以 安心的编写安心的使用
    • 你在 写的时候保证了函数的纯度,只是 单纯实现自己的业务逻辑即可, 不需要关心传入的内容是如何获得的或者 依赖其他的外部变量是否已经发生了修改
    • 你在 用的时候,你确定 你的输入内容不会被任意篡改,并且 自己确定的输入,一定会 有确定的输出
  • React中就要求我们无论是函数还是class声明一个组件,这个组件都必须像纯函数一样,保护它们的props不被修改
  • 在接下来学习redux中,reducer也被要求是一个纯函数.

二、Redux的基本使用

为什么需要redux

  • JavaScript开发的应用程序,已经变得越来越复杂了:
    • JavaScript需要 管理的状态越来越多,越来越复杂
    • 这些状态包括 服务器返回的数据、缓存数据、用户操作产生的数据等等,也包括一些 UI 的状态,比如 某些元素是否被选中,是否显示加载动效,当前分页
  • 管理不断变化的state是非常困难的:
    • 状态之间相互会存在依赖,一个状态的变化会引起另一个状态的变化,View页面也有可能会引起状态的变化
    • 当应用程序复杂时, state在什么时候,因为什么原因而发生了变化,发生了怎么样的变化,会变得非常 难以控制和追踪
  • React是在视图层帮助我们解决了DOM的渲染过程,但是State依然是留给我们自己来管理:
    • 无论是 组件定义自己的state,还是 组件之间的通信通过props进行传递;也包括 通过Context进行数据之间的共享
    • React主要负责帮助我们 管理视图,state如何维护最终 还是我们自己来决定
  • Redux就是一个帮助我们管理State的容器:Redux是 JavaScript 的状态容器,提供了 可预测的状态管理
  • Redux除了和React一起使用之外,它也可以和其他界面库一起来使用(比如Vue),并且它非常小(包括依赖在内,只有2kb)

Redux的核心理念 - Store

  • Redux的核心理念非常简单
  • 比如我们有一个朋友列表需要管理
    • 如果我们 没有定义统一的规范来操作这段数据 那么 整个数据的变化就是无法跟踪的
    • 比如页面的某处通过products.push的方式增加了一条数据
    • 比如另一个页面通过products[0].age = 25修改了一条数据
  • 整个应用程序错综复杂 当出现bug时 很难跟踪到底哪里发生的变化
  •  

Redux的核心理念 - action

  • Redux要求我们通过action来更新数据:
    • 所有数据的变化,必须通过 派发(dispatch)action来更新
    • action是一个普通的 JavaScript 对象,用来描述这次 更新的type和content
  • 比如下面就是几个更新friends的action:
    • 强制使用action的好处是可以 清晰的知道数据到底发生了什么样的变化,所有的数据变化都是可跟踪,可预测的
    • 当然 目前我们的 action是固定的对象
    • 真实应用中 我们 会通过函数来定义 返回一个action

Redux的核心理念 - reducer

  • 但是如何将state和action联系在一起呢?答案就是reducer
    • reducer是 一个纯函数
    • reducer做的事情就是 将传入的state和action结合起来生成一个新的state

Redux的三大原则

  • 单一数据源
    • 整个应用程序的 state被存储在一颗object tree中,并且 这个object tree只存储在一个 store
    • Redux 并没有强制让我们不能创建多个Store,但是 那样做并不利于数据的维护
    • 单一的数据源可以让整个应用程序的state变得 方便维护、追踪、修改
  • State是只读的
    • 唯一修改State的方法一定是触发action,不要试图在其他地方通过任何的方式来修改State
    • 这样就确保了View或网络请求都 不能直接修改state,它们只能 通过action来描述自己想要如何修改state
    • 这样可以 保证所有的修改都被集中化处理,并且 按照严格的顺序来执行,所以 不需要担心race condition(竟态)的问题
  • 使用纯函数来执行修改
    • 通过reducer将 旧state和 actions联系在一起,并且 返回一个新的State
    • 随着 应用程序的复杂度增加,我们 可以将reducer拆分成多个小的reducers分别操作不同state tree的一部分
    • 但是 所有的reducer都应该是纯函数,不能产生任何的副作用

Redux测试项目搭建

  • 安装redux:
  • 1.创建一个新的项目文件夹:learn-redux
  • 2.创建src目录,并且创建index.js文件
  • 3.修改package.json可以执行index.js

Redux的使用过程

  • 1.创建一个对象,作为我们要保存的状态:
  • 2.创建Store来存储这个state
    • 创建store时必须创建reducer;
    • 我们可以通过 store.getState 来获取当前的state;
  • 3.通过action来修改state
    • 通过dispatch来派发action;
    • 通常action中都会有type属性,也可以携带其他的数据;
  • 4.修改reducer中的处理代码
    • 这里一定要记住,reducer是一个纯函数,不需要直接修改state;
  • 5.可以在派发action之前,监听store的变化

Redux结构划分

  • 如果我们将所有的逻辑代码写到一起,那么当redux变得复杂时代码就难以维护
    • 接下来,我会对代码进行拆分,将store、reducer、action、constants拆分成一个个文件
    • 创建 store/index.js文件
    • 创建 store/reducer.js文件
    • 创建 store/actionCreators.js文件
    • 创建 store/constants.js文件
  • 注意:node中对ES6模块化的支持
    • 从node v13.2.0开始,node才对ES6模块化提供了支持
    • node v13.2.0之前,需要进行如下操作:
      • 在package.json中添加属性: "type": "module";
      • 在执行命令中添加如下选项:node --experimental-modules src/index.js;
    • node v13.2.0之后,只需要进行如下操作
      • 在package.json中添加属性: "type": "module";
  • 注意:导入文件时,需要跟上. js 后缀名

Redux使用流程

  • 我们已经知道了redux的基本使用过程,那么我们就更加清晰来认识一下redux在实际开发中的流程:

Redux官方图

三、React结合Redux

redux融入react代码

  • 目前redux在react中使用是最多的,所以我们需要将之前编写的redux代码,融入到react当中去。
  • 这里我创建了两个组件:
    • Home组件:其中会展示当前的counter值,并且有一个+1和+5的按钮;
    • Profile组件:其中会展示当前的counter值,并且有一个-1和-5的按钮
  • 核心代码主要是两个:
    • 在 componentDidMount 中定义数据的变化,当数据发生变化时重新设置 counter
    • 在发生点击事件时,调用store的dispatch来派发对应的action;

react-redux使用

  • 开始之前需要强调一下,redux和react没有直接的关系,完全可以在React, Angular, Ember, jQuery, or vanilla JavaScript 中使用Redux
  • 尽管这样说,redux依然是和React库结合的更好,因为他们是通过state函数来描述界面的状态,Redux可以发射状态的更新,让他们作出相应的操作.
  • 虽然我们已经实现了connect、Provider这些帮助我们完成连接redux、react的辅助工具,但是实际上redux官方帮助我们提供了 react-redux 的库,可以直接在项目中使用,并且实现的逻辑会更加的严谨和高效
  • 安装react-redux:npm install react-redux
  • 在需要使用store的组件中进行这样的操作:
    • connect接收两个参数
      • 参数1:哪 些数据需要映射到About中
      • 参数2: dispatch

react-redux源码导读

四、Redux的异步操作

组件中异步操作

  • 在之前简单的案例中,redux中保存的counter是一个本地定义的数据
    • 我们可以直接通过同步的操作来dispatch action state就会被立即更新。
    • 在开发中, redux中保存的很多数据可能来自服务器,我们需要进行 异步的请求,将数据保存到redux
  • 在之前学习网络请求的时候我们讲过,网络请求可以在class组件的componentDidMount中发送,所以我们可以有这样的结构:

redux中异步操作

  • 上图有一个缺陷:
    • 我们必须 将网络请求的异步代码放到组件的生命周期中来完成
    • 事实上, 网络请求到的数据也属于我们状态管理的一部分,更好的一种方式应该是 将其也交给redux来管理
  • 但是在redux中如何可以进行异步的操作呢?
    • 答案就是使用中间件 (Middleware)
    • 学习过Express或Koa框架对中间件的概念一定不陌生
    • 在这类框架中,Middleware可以帮助我们 在请求和响应之间嵌入一些操作的代码,比如cookie解析、日志记录、文件压缩等操作

理解中间件

  • redux也引入了中间件(Middleware)的概念:
    • 这个中间件的目的是 在dispatch的action和最终达到的reducer之间,扩展一些自己的代码
    • 比如 日志记录、调用异步接口、添加代码调试功能等等
  • 我们现在要做的事情就是发送异步的网络请求,所以我们可以添加对应的中间件:
    • 这里 官网推荐的、包括演示的网络请求的中间件使用 redux-thunk
  • redux-thunk是如何做到让我们可以发送异步的请求呢
    • 我们知道, 默认情况下的dispatch(action),action需要是一个 JavaScript 的对象
    • redux-thunk可以让 dispatch(action函数),action 可以是一个函数
    • 该函数会被调用,并且会传给 这个函数一个dispatch函数和getState函数
      • dispatch函数用于我们之后再次派发action
      • getState函数考虑到我们之后的一些操作需要依赖原来的状态,用于让我们可以获取之前的一些状态

如何使用redux-thunk

  • 1.安装redux-thunk
    • Npm install redux-thunk
  • 2.在创建store时传入应用了middleware的enhance函数
    • 通过applyMiddleware来结合多个Middleware,返回一个enhancer;
    • 将enhancer作为第二个参数传入到createStore中;
  • 3.定义返回一个函数的action:
    • 注意:这里不是返回一个对象了,而是一个函数;
    • 该函数在dispatch之后会被执行

五、redux-devtool

redux-devtools

  • redux可以方便的让我们对状态进行跟踪和调试,那么如何做到呢?
    • redux官网为我们 提供了redux-devtools的工具
    • 利用这个工具,我们可以知道 每次状态是如何被修改的,修改前后的状态变化等等
  • 安装该工具需要两步:
    • 第一步:在对应的浏览器中安装相关的插件(比如Chrome浏览器扩展商店中搜索Redux DevTools即可);
    • 第二步:在redux中继承devtools的中间件

六、reducer的模块拆分

Reducer代码拆分

  • 我们先来理解一下,为什么这个函数叫reducer?
  • 我们来看一下目前我们的reducer:
    • 当前这个reducer既有处理counter的代码,又有处理home页面的数据
    • 后续counter相关的状态或home相关的状态会进一步变得更加复杂
    • 我们也会继续添加其他的相关状态,比如购物车、分类、歌单等等
    • 如果将所有的状态都放到一个reducer中进行管理,随着项目的日趋庞大,必然会造成代码臃肿、难以维护
  • 因此,我们可以对reducer进行拆分:
    • 我们先抽取一个对counter处理的reducer
    • 再抽取一个对home处理的reducer
    • 将它们合并起来

Reducer文件拆分

  • 目前我们已经将不同的状态处理拆分到不同的reducer中,我们来思考:
    • 虽然已经放到不同的函数了,但是这些函数的处理依然是在同一个文件中,代码非常的混乱
    • 另外关于reducer中用到的constant、action等我们也依然是在同一个文件中

combineReducers函数

  • 目前我们合并的方式是通过每次调用reducer函数自己来返回一个新的对象
  • 事实上,redux给我们提供了一个 combineReducers函数 可以方便的让我们对多个reducer进行合并:
  •  

  • 那么combineReducers是如何实现的呢?
    • 它也是 将我们传入的reducers合并到一个对象中,最终 返回一个combination的函数(相当于我们之前的reducer函数了)
    • 执行combination函数的过程中,它会 通过判断前后返回的数据是否相同来决定返回之前的state还是新的state
    • 新的state会触发订阅者发生对应的刷新,旧的state可以有效的阻止订阅者发生刷新

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

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

相关文章

亚马逊云科技Serverless数据分析,助力猎豹移动构建更高性价比数据仓库

也许你也听过这样一句话:“21世纪什么最贵?人才!”当数字经济全面席卷而来,这个问题的答案不可置否地变为了“数据”。通过数据分析获取近乎实时的洞察,以驱动业务的全流程,是企业数字化转型的必经之路。借…

【文末送书】微服务拆分规范

目录 一. 🦁 什么是微服务?二. 🦁 拆分模型Ⅰ. 压力模型拆分1. 垂直拆分(Vertical Decomposition)2. 水平拆分(Horizontal Decomposition)3. 动态拆分(Dynamic Decomposition&#x…

初识网络之http协议

目录 一、http协议含义 二、 认识URL 三、urlencode与urldecode 1. urlencode 2. urldecode 四、http协议响应与请求格式 1. http协议请求格式 2.http协议响应格式 3. http请求实际形式 3.1 程序准备 3.2 浏览器发起请求 3.3 请求行内容 3.4 请求报头内容 4. htt…

2022年国赛高教杯数学建模E题小批量物料的生产安排解题全过程文档及程序

2022年国赛高教杯数学建模 E题 小批量物料的生产安排 原题再现 某电子产品制造企业面临以下问题:在多品种小批量的物料生产中,事先无法知道物料的实际需求量。企业希望运用数学方法,分析已有的历史数据,建立数学模型&#xff0c…

mysql联合索引详解

比较简单的是单列索引(btree)。遇到多条件查询时,不可避免会使用到多列索引。联合索引又叫复合索引。 btree结构如下: 每一个磁盘块在mysql中是一个页,页大小是固定的,mysql innodb的默认的页大小是16k&a…

【工作中遇到的性能优化问题】

项目场景: 页面左侧有一列表数据,点击列表项会查对应的表格数据和表单信息(表单是根据数据配置生成的),并在右侧展示。如果数据量大,则非常卡。 需要对此页面进行优化。 问题描述 问题一、加载左侧数据时…

systemV的工作原理+原理代码

概念 我们知道进程间的通信有管道的方式进程通信管道制作_云的小站的博客-CSDN博客 但是我们的管道通信其实属于一种取巧的方式,利用了打开的文件可读写的特性上,两个进程对此分别进行读写操作就会产生所谓的通信现象,但是外面的管道依旧得…

【社区图书】快速入门go程序开发——《Go程序开发实战宝典》书评

《Go程序开发实战宝典》书评 一、介绍二、简要概述三、内容分析3.1、第一部分:Go语言基础知识3.2、第二部分:介绍服务端开发经常需要处理的问题3.3、第三部分:Go语言开发实践实战案例 四、我的看法和评价4.1、对本书整体评价4.2、我对这本书的…

Office Visio 2021安装

哈喽,大家好。今天一起学习的是Visio 2021的安装,这是一个绘制流程图的软件,用有效的绘图表达信息,比任何文字都更加形象和直观。Office Visio 是office软件系列中负责绘制流程图和示意图的软件,便于IT和商务人员就复杂…

Vue.js 中的插槽和动态组件

Vue.js 中的插槽和动态组件 Vue.js 是一款流行的 JavaScript 框架,它提供了一种简单而灵活的方式来构建交互式 Web 应用程序。在 Vue.js 中,插槽和动态组件是两个常用的概念。它们可以帮助开发者更方便地组织和管理组件的结构和行为。但是这两个概念有什…

(opencv)图像几何变换——平移

图像的平移操作是将图像的所有像素坐标进行水平或垂直方向移动,也就是将所有像素点按照给定的偏移量在水平方向沿x轴、垂直方向上沿y轴移动。平移变换分为两种类型:图像大小变化与图像大小不变。第一种类型保证图像平移的完整信息,第二种图像…

Vue.js 中的 $refs 和 $emit 有什么关系?

Vue.js 中的 $refs 和 $emit 有什么关系? 在 Vue.js 中,$refs 和 $emit 都是非常常用的 API。$refs 用于访问组件、元素和子组件等,而 $emit 则用于在组件之间进行通信。本文将会从语法、使用方式、适用场景等方面进行介绍,并探讨…

接招吧! selenium环境+元素定位大法

selenium 与 webdriver Selenium 是一个用于 Web 测试的工具,测试运行在浏览器中,就像真正的用户在手工操作一样。支持所有主流浏览器 WebDriver 就是对浏览器提供的原生API进行封装,使其成为一套更加面向对象的Selenium WebDriver API。 …

【数据结构与算法分析】使用C语言实现队列的两种(带头结点与不带头结点)链式存储,并且给出一种循环队列的设计思想

文章目录 前言队列实现带头结点单向队列不带头结点单向队列循环队列 总结 前言 当我们编写程序时,经常需要处理各种数据结构。队列是一种常见的数据结构,它有着广泛的应用场景。队列的基本操作包括入队和出队,应用于模拟等待队列、消息队列、…

LVS-DR集群

LVS-DR集群 一.LVS-DR工作原理 1.数据包流向 数据包流向分析: (1)客户端发送请求到 Director Server(负载均衡器),请求的数据报文(源 IP 是 CIP,目标 IP 是 VIP)到达内核空间。 &…

Flowable工作流入门完整SpringBoot案例

文章目录 一 、Flowable 的出现是为了什么二、Flowable 的优势三、常见的Java类/实例3.1 ProcessEngine3.2 RepositoryService3.3 ProcessDefinition3.4 Deployment3.5 RuntimeService3.6 ProcessInstance3.7 TaskService3.8 JavaDelegate3.9 其他 四、核心数据库表4.1 数据库4…

CET4写译学习

学习记录笔记: 05.四级写译技巧(上)_哔哩哔哩_bilibili 不会的东西不要往上写。寻找可以替换的词。 保证写的所有内容都是正确的。 切题,论证清楚。 要有自己的观点,然后去论证。 词汇,语法,句子结构都整好。 文…

【软件测试】接口测试工具APIpost

说实话,了解APIpost是因为,我的所有接口相关的文章下,都有该APIpost水军的评论,无非就是APIpost是中文版的postman,有多么多么好用,虽然咱也还不是什么啥网红,但是不知会一声就乱在评论区打广告…

Linux日志

rsyslog系统日志管理 哪类程序产生的什么日志放到什么地方 处理日志的进程 第一类: rsyslogd:系统专职日志程序,处理绝大部分日志记录,系统操作相关的信息,如登录信息,程序启动关闭相关信息&#xff0c…

C#语言实现4K图片放大缩小和平移显示性能的速度测试

在介绍“熊猫视图.Net图形控件”系列文章中, 【“熊猫视图.Net图形控件”介绍链接】https://blog.csdn.net/mosangbike/article/details/126026801有对显示图像文件的测试结果,当时测试的不太严谨。今天抽时间详细测试了一下。 从网上找了一张Jpg图像作…