Redux中间件源码解析与实现
基本介绍
本文中涉及到的关键npm包的版本信息如下:
react 的版本为18.2.0
redux的版本为4.1.2
redux-thunk版本为2.4.2
redux-promise版本为0.6.0
redux-logger版本为3.0.6
在Redux源码解析与实现(一)Redux源码解析与实现(二)这两篇文章中,详细讲解了怎么实现一个Redux
的核心功能,而Redux
默认只能够处理plainObject
的参数,所以我们需要引用各种中间件来加强dispatch
,使其能够传递异步函数或者是promise
或者是其他的能力,比如说打日志。每个中间件往往也只做一件特定的事情,比如redux-thunk
就是可以处理函数redux-logger
可以打印出日志redux-promise
可以处理action
为promise
的情况。而且下一个中间件的接受的action
为上一个中间件的处理的结果,如下图所示:
基本使用
我们建一个简单的demo,看看redux-promise、redux-promise、redux-logger
是如何使用的。我们只需要在createStore
方法中传递经过applyMiddleware
处理过的中间件即可,核心代码如下:
// store/index.js
import thunk from 'redux-thunk'
import logger from 'redux-logger'
import promise from 'redux-promise'const store = createStore(rootReducer,// logger 要在thunk的后面,dispatch接受的参数可能是函数经过thunk处理之后再return一个planObject再交给logger处理applyMiddleware(promise, thunk, logger)
)
// ReduxPage.jsx
import React from "react";
import store from "../store";
import './index.css'export default class ReduxPage extends React.Component {componentDidMount() {// 新增订阅,在这里订阅的逻辑就是当状态管理库store中数据更新时,组件也需要更新一下this.unsubscribe = store.subscribe(() => {this.forceUpdate()})}componentWillUnmount() {// 组件卸载 -> 自然需要取消订阅this.unsubscribe()}add = () => {store.dispatch({type: "ADD"})}asyncAdd = () => {store.dispatch((dispatch, getState) => {console.log('getState pre:', getState());// 使用setTineout模拟后端请求setTimeout(() => {// 拿到服务端数据,处理完数据之后再dispatchdispatch({type: "ADD", payload: 10})console.log('getState after:', getState());}, 1000)})}promiseAdd = () => {// dispatch接受一个promise的参数// store.dispatch(Promise.resolve({type: 'ADD', payload: 20}))store.dispatch(new Promise((resolve, reject) => {setTimeout(() => {resolve({type: 'ADD', payload: 20})}, 1000)}))}minus = () => {store.dispatch({type: "MINUS"})}setName = () => {store.dispatch({type: "SET_NAME", payload: `hyy${Math.random()}`})}setAge = () => {store.dispatch({type: "SET_AGE", payload: Math.random() * 100})}render() {return (<div><div> ReduxPage </div><div><span className="box">count: {store.getState().count}</span><span className="box">name: {store.getState().userInfo.name}</span><span className="box">age: {store.getState().userInfo.age}</span></div><div className="wrap"><button onClick={this.add}>add</button><button onClick={this.asyncAdd}>asyncAdd</button><button onClick={this.promiseAdd}>promiseAdd</button><button onClick={this.minus}>minus</button></div><div className="wrap"><button onClick={this.setName}>setName</button><button onClick={this.setAge}>setAge</button></div></div>);}
}
经过Redux源码解析与实现(一)Redux源码解析与实现(二)我们知道在用户在dispatch
的时候会依次执行各个中间件,而中间件会接受到dispatch & getStore
这个对象以读写store
中的数据然后返回值给下一个中间件执行。直至最后应该得到一个plainObject
传递给原始的dispatch
执行。其中通过applyMiddleware
接受到的若干个中间件函数数组则是使用了函数式编程思想中函数聚合的方式依次执行。
redux中间件的基本使用demo
源码分析
经过上面的分析,所有的中间件的代码架子均如下所示:
function xxx({getState, dispatch}) {// next就是聚合函数componse中的下一个要执行的函数(reducer中的func)return (next) => (action) => {// todo...// returnValue就是该中间件处理完之后返回给下一个中间件的值return returnValue;};
}
代码实现
redux-promise
这个中间件主要是判断当前传递的参数即action是否是promise
,如果是的话,那我们就需要通过promise
的方式获取值然后返回给下一个中间件,如果不是的话,那就直接运行当前函数即可。其代码基本实现如下:
function promise({getState, dispatch}) {return (next) => (action) => {// 判断传递进来的action是否为promisereturn isPromise(action) ? action.then(dispatch) : next(action);};
}
redux-thunk
这个中间件主要是判断当前的action
是否是函数,如果是函数的话,那就执行该函数并将dispatch & getStore
两个操作状态管理库的API传递给这个函数,这个函数执行的结果return
给下一个中间件的入参,如果不是一个函数,那就将action
传递给当前函数执行&返回结果给下一个中间件其主要核心代码如下:
// 自定义thunk中间件
// 中间件接受的参数就是middlewareAPI即getState & dispatch 让中间件有操作状态管理库的权限
function thunk({getState, dispatch}) {// ! next就是聚合函数componse中的下一个要执行的函数(reducer中的func)而不是下一个中间件return (next) => (action) => {if (typeof action === "function") {// 如果接受到一个函数的话就执行这个函数 & 把dispatch和getState作为参数传递给这个函数// 所以业务代码传递给dispatch的函数参数可以接受dispatch, getState这两个参数return action(dispatch, getState);}// 如果action不是函数那就正常执行 并把当前函数执行的值return给下一个中间件return next(action);};
}
redux-logger
这个中间件主要是在dispatch
的时候打印出了一些日志,便于我们做数据追踪,我们可以在数据修改之前(即dispatch
之前打印出原始数据 )+ action
动作和数据被修改之后(即dispatch
之后的store
中的数据)
其核心代码如下:
// 自定义logger 中间件
function logger({getState, dispatch}) {return (next) => (action) => {console.log("------------------------------------------");console.log("prev state", getState());console.log(`${action.type ? `ACTION: ${action.type + "已被执行~"}` : '接受到action为非plainObject'}`);const returnValue = next(action);console.log("next state", getState());console.log("------------------------------------------");return returnValue;};
}
我们将示例中的中间件部分换成我们自己开发的中间件查看下效果:
线上demo: redux中间件的基本实现
相关资料
redux
redux-thunk
redux-logger
redux-promise
Redux源码解析与实现(一)
Redux源码解析与实现(二)
redux中间件的基本使用demo
redux中间件的基本实现
相关文章:

Redux中间件源码解析与实现
基本介绍 本文中涉及到的关键npm包的版本信息如下: react 的版本为18.2.0 redux的版本为4.1.2 redux-thunk版本为2.4.2 redux-promise版本为0.6.0 redux-logger版本为3.0.6 在Redux源码解析与实现(一)Redux源码解析与实现(二&…...

关于rsync用不了之后
1.尝试找出rsync使用错误原因: 我遇见一个问题:rsync:read errors mapping:communication error on send (70),我查了一下这个问题很大可能是网络链接导致的,然后我用nslookup指令查看了/train2…...
由一个多线程并发保存而引发的思考
1. 问题描述 问题描述,现A表(用户查看实验的次数)有user_id和exp_id两个字段,其中user_id加exp_id不唯一。B表有user_id,exp_id以及exp_num三个字段,其中user_id加exp_id唯一(表中未建唯一索引)。 现需要将A表的数据同步到B表,单机模式通过定时任务同步。A表的数据有…...
python-vlc
文章目录 关于 python-vlc安装使用关于 python-vlc Python vlc bindings github : https://github.com/oaubert/python-vlcPython bindings (ctypes-based) for VLC http://olivieraubert.net/vlc/python-ctypes/关于 VLC 可参考 macOS - 安装使用 VLC https://blog.csdn.net/…...

2023长城杯 web部分题目(seekingeasy_extension)
seeking 下载题目附件得到: <?php error_reporting(0); header("HINT:POST n range(1,10)");$image $_GET[image]; echo "这里什么也没有,或许吧。"; $allow range(1, 10); shuffle($allow); if (($_POST[n] $allow[0])) …...

2-1 张量数据结构
张量概念 张量是什么? 单个元素叫标量(scalar),一个序列叫向量(vector),多个序列组成的平面叫矩阵(matrix),多个平面组成的立方体叫张量(tensor&…...
QSqlQuery查询语句
SqlQuery 封装了在 QSqlDatabase 上执行的 SQL 查询中创建、导航和检索数据所涉及的功能。 可用于执行 DML(数据操作语言)语句,如 SELECT、INSERT、UPDATE 和 DELETE, 以及 DDL(数据定义语言)语句ÿ…...

用c语言编写出三底模型
以下是一个用C语言实现三底模型的示例代码。这个程序通过循环遍历输入的股票数据,判断是否出现三底形态,如果是,则输出买入信号,否则输出卖出信号。 c语言 #include <stdio.h> #include <stdlib.h> // 判断是否出现…...
15 Python使用MySQL
概述 在上一节,我们介绍了如何在Python中使用网络,包括:套接字编程、socketserver等内容。在这一节,我们将介绍如何在Python中使用MySQL。MySQL是最流行的关系型数据库管理系统之一,由瑞典MySQL AB公司开发,…...
3、Nginx 常用的命令和配置文件
文章目录 3、nginx 常用的命令和配置文件3.1 nginx 常用的命令:3.2 nginx.conf 配置文件3.2.1 地址3.2.2 内容3.2.2 vim不正常退出后再次打开信息提示解决方法 3.3 第一部分:全局块3.4 第二部分:events 块3.4 第三部分:http 块①、…...
python经典百题之兔子出生问题
这是一个经典的 Fibonacci 数列问题,可以通过递归或循环来解决。 递归方法: 由题意可知,第 n 个月的兔子数等于第 n-1 个月的兔子数加上第 n-2 个月的兔子数。设 f(n) 表示第 n 个月的兔子数,则有: f(n) f(n-1) f…...

不定积分的概念和性质
目录 原函数 不定积分 不定积分的几何意义 原函数的存在定理 不定积分的性质 不定积分是微积分的一个关键部分,它涉及到一个函数的不定积分的计算。不定积分可以理解为求一个函数的原函数,也被称为反导数。原函数是一个函数,使得该函数的…...

远程访问服务器JupyterLab的配置方法
远程访问服务器JupyterLab的配置方法 环境及工具注意 基本步骤生成密码生成并修改配置文件*错误:jupyter localhost 已拒绝连接*后台运行jupyter后台关闭 其实就是在服务器运行JupyterLab,然后在本地浏览器访问 环境及工具 服务器:Ubuntu 1…...

Java native 关键字
如你在看 JDK 的源代码的时候,大概率会看到很多方法使用了 native 关键字。 下面是 String 对象 JDK 中的源代码,就带有了一个 native 关键字。 native 是干什么用的 简单来说就是 Java 的 native 方法的实现不是用 Java 实现的,可能在其他…...

【线性代数】沉浸式线性代数在线学习网站
地址:http://immersivemath.com/ila/index.html 这是全球第一本带交互式图形的线性代数教材,作者是 J. Strm, K. strm, and T. Akenine-Mller。 全书一共十章,各章节内容如下: 接下来我将对各章节进行简单的总结,另外…...
Kotlin中特性、数据类、伴生对象、顶层函数
Kotlin中的函数参数和属性声明 在 Kotlin 中,函数参数和属性有不同的声明方式和行为。这些特性使得 Kotlin 代码更加安全、易于理解和维护。 函数参数的只读性 fun sum(a: Int, b: Int): Int {var modifiedA aif (modifiedA > 0) {modifiedA 1}//三元表达式v…...

《PostgreSQL物化视图:创建、维护与应用》
🌷🍁 博主猫头虎(🐅🐾)带您 Go to New World✨🍁 🐅🐾猫头虎建议程序员必备技术栈一览表📖: 🛠️ 全栈技术 Full Stack: 📚…...
shell脚本之test命令
shell脚本之test命令 数值比较:2. 字符串比较:3. 文件测试:4. 逻辑操作:5. 其他测试: test命令在Shell脚本中用于进行条件测试和条件判断。它用于检查文件、字符串和数值的各种条件,并返回一个状态码&#…...

JAVA设计模式8:装饰模式,动态地将责任附加到对象上,扩展对象的功能
作者主页:Designer 小郑 作者简介:3年JAVA全栈开发经验,专注JAVA技术、系统定制、远程指导,致力于企业数字化转型,CSDN博客专家,阿里云社区专家博主,蓝桥云课讲师。 目录 一、什么是装饰模式二、…...
Linux学习之MySQL备份
xtrabackup资源下载 完全备份与恢复 # 1.物理备份与恢复 # 冷备份,需停止数据库服务 适合线下服务器。 [rootmysql50 ~]# systemctl stop mysqld [rootmysql50 ~]# mkdir /bakdir [rootmysql50 ~]# cp -r /var/lib/mysql /bakdir/mysql.bak [rootmysql50 ~]# cd /…...
变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析
一、变量声明设计:let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性,这种设计体现了语言的核心哲学。以下是深度解析: 1.1 设计理念剖析 安全优先原则:默认不可变强制开发者明确声明意图 let x 5; …...

手游刚开服就被攻击怎么办?如何防御DDoS?
开服初期是手游最脆弱的阶段,极易成为DDoS攻击的目标。一旦遭遇攻击,可能导致服务器瘫痪、玩家流失,甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案,帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...
【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密
在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...

如何在看板中有效管理突发紧急任务
在看板中有效管理突发紧急任务需要:设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP(Work-in-Progress)弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中,设立专门的紧急任务通道尤为重要,这能…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个生活电费的缴纳和查询小程序
一、项目初始化与配置 1. 创建项目 ohpm init harmony/utility-payment-app 2. 配置权限 // module.json5 {"requestPermissions": [{"name": "ohos.permission.INTERNET"},{"name": "ohos.permission.GET_NETWORK_INFO"…...
LLM基础1_语言模型如何处理文本
基于GitHub项目:https://github.com/datawhalechina/llms-from-scratch-cn 工具介绍 tiktoken:OpenAI开发的专业"分词器" torch:Facebook开发的强力计算引擎,相当于超级计算器 理解词嵌入:给词语画"…...

c#开发AI模型对话
AI模型 前面已经介绍了一般AI模型本地部署,直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型,但是目前国内可能使用不多,至少实践例子很少看见。开发训练模型就不介绍了&am…...
Typeerror: cannot read properties of undefined (reading ‘XXX‘)
最近需要在离线机器上运行软件,所以得把软件用docker打包起来,大部分功能都没问题,出了一个奇怪的事情。同样的代码,在本机上用vscode可以运行起来,但是打包之后在docker里出现了问题。使用的是dialog组件,…...

elementUI点击浏览table所选行数据查看文档
项目场景: table按照要求特定的数据变成按钮可以点击 解决方案: <el-table-columnprop"mlname"label"名称"align"center"width"180"><template slot-scope"scope"><el-buttonv-if&qu…...
ubuntu22.04 安装docker 和docker-compose
首先你要确保没有docker环境或者使用命令删掉docker sudo apt-get remove docker docker-engine docker.io containerd runc安装docker 更新软件环境 sudo apt update sudo apt upgrade下载docker依赖和GPG 密钥 # 依赖 apt-get install ca-certificates curl gnupg lsb-rel…...