React@16.x(60)Redux@4.x(9)- 实现 applyMiddleware
目录
- 1,applyMiddleware 原理
- 2,实现
- 2.1,applyMiddleware
- 2.1.1,compose 方法
- 2.1.2,applyMiddleware
- 2.2,修改 createStore
接上篇文章:Redux中间件介绍。
1,applyMiddleware 原理
Redux 应用中间件方式:
const store = createStore(reducer, applyMiddleware(logger1, logger2));
实际会转为下面的方式执行:
const store = applyMiddleware(logger1, logger2)(createStore)(reducer)
applyMiddleware会确定用到的中间件;它会返回一个用来创建仓库的函数A,参数是createStore;- 函数 A,会返回创建仓库的函数B,和
createStore函数差不多。 - 函数B,会对中间件函数做处理,并修改原始
dispatch。
大致相当于:
export const applyMiddleware = (...middlewares) => {// 用来创建的仓库的函数return (createStore) => {// 创建仓库的函数return (reducer, defaultState) => {const store = createStore(reducer, defaultState)const dispatch = '经过 middlewares 处理的 store.dispatch'return {...store,dispatch}}}
}
2,实现
2.1,applyMiddleware
上篇文章介绍了 中间件函数 的写法和多个中间件函数的执行顺序。
基于此,实现这个 dispatch 流转的逻辑,并得到最终的 dispatch 即可完成 applyMiddleware。
2.1.1,compose 方法
关键要实现
const resultDispatch = logger1(logger2(logger3(store.dispatch)))
实现:
/*** @param {...any} funcs* @returns {function}*/
export const compose = (...funcs) => {if (funcs.length === 0) {return (args) => args;} else if (funcs.length === 1) {return funcs[0];}return (...args) => {let lastReturn = null;for (let i = funcs.length - 1; i >= 0; i--) {const func = funcs[i];if (i === funcs.length - 1) {lastReturn = func(...args);} else {lastReturn = func(lastReturn);}}return lastReturn;};
};
// 测试代码
const add = (n) => {return n + n;
};const mult = (n) => {return n * n;
};const b = compose(add, mult);
console.log(b(3)); // 先乘后加,18
可使用 Array.reduce 简化:
export const compose = (...funcs) =>funcs.reduce((prev, next) =>(...args) =>prev(next(...args)));
2.1.2,applyMiddleware
import { compose } from "./compose";
export const applyMiddleware = (...middlewares) => {return (createStore) => {return (reducer, defaultState) => {const store = createStore(reducer, defaultState);let dispatch = () => {throw new Error("目前还不能使用 dispatch");};// 传递给中间件函数的 store 只有这2个属性。const simpleStore = {getState: store.getState,dispatch: (...args) => dispatch(...args), // 每个中间件函数的 dispatch 都是上一个中间件组装后的};// 获取用于创建 dispatch 的函数const dispatchProducts = middlewares.map((m) => m(simpleStore));// 重新组装后的 dispatchdispatch = compose(...dispatchProducts)(store.dispatch);return {...store,dispatch,};};};
};
2.2,修改 createStore
之前实现的 createStore,没有对可能的第3个函数做处理。这里补充下:
- 如果第2个参数是
applyMiddleware,那说明没有defaultState。
这里就简单判断写第2个参数是不是函数,实际源码中
defaultState也可以通过一个函数创建。
export const createStore = (reducer, defaultState, enhancer) => {// enhancer 表示 applymiddleware 返回的函数。if (typeof defaultState === 'function') {enhancer = defaultStatedefaultState = undefined}if (typeof enhancer === 'function') {enhancer(createStore)(reducer, defaultState)}// 其他剩余代码没有做变动。// ...
}
完整代码
export const createStore = (reducer, defaultState, enhancer) => {// enhancer 表示 applymiddleware 返回的函数。if (typeof defaultState === "function") {enhancer = defaultState;defaultState = undefined;}if (typeof enhancer === "function") {enhancer(createStore)(reducer, defaultState);}let currentReducer = reducer;let currentState = defaultState;let listeners = [];const dispatch = (action) => {if (typeof action !== "object" || Object.getPrototypeOf(action) !== Object.prototype) {throw new Error("action 必须是一个 plain Object");}if (action.type === undefined) {throw new Error("action 必须有 type 属性");}currentState = currentReducer(currentState, action);// 每次更新时,遍历监听器。for (const listener of listeners) {listener();}};const getState = () => {return currentState;};const subscribe = (listener) => {listeners.push(listener);let isRemove = false;// 取消监听return () => {if (isRemove) {return;} else {isRemove = true;listeners = listeners.filter((f) => f !== listener);}};};// createStore 创建时会调用一次。dispatch({type: `@@redux/INIT${getRandomString}`,});return {dispatch,getState,subscribe,};
};function getRandomString() {return Math.random().toString(36).substring(2, 8).split("").join(".");
}
以上。
相关文章:
React@16.x(60)Redux@4.x(9)- 实现 applyMiddleware
目录 1,applyMiddleware 原理2,实现2.1,applyMiddleware2.1.1,compose 方法2.1.2,applyMiddleware 2.2,修改 createStore 接上篇文章:Redux中间件介绍。 1,applyMiddleware 原理 R…...
level 6 day1 Linux网络编程之网络基础
v1 网络的历史和分层 TCP 是可靠传输,IP协议是不可靠传输 网络的体系结构 网络分层的思想: OSI体系结构 两层交换机是指数据链路层的交换 三层交换是指网络层这边的交换 四层模型 蓝色的字 是由手机发给PC机,由传输层来决定应该交给哪一…...
PostgreSQL UPDATE 命令
PostgreSQL UPDATE 命令 PostgreSQL 是一种功能强大的开源对象关系型数据库管理系统(ORDBMS),它使用并扩展了SQL语言。在处理数据库时,我们经常需要更新现有的记录。在PostgreSQL中,UPDATE命令用于修改表中的现有记录…...
什么? CSS 将支持 if() 函数了?
CSS Working Group 简称 CSSWG, 在近期的会议中决定将 if() 添加到 CSS Values Module Level 5 中。 详情可见:css-meeting-bot 、[css-values] if() function 当我看到这个消息的时候,心中直呼这很逆天了,我们知道像 less 这些 css 这些预…...
function calling实现调用理杏仁api获取数据
LLM是不存在真正逻辑的且并不是知晓万事万物的(至少目前是这样)在很多更垂直的环境下LLM并不能很好的赋能。 function calling的实现使LLM可以对接真正的世界以及真正有逻辑的系统,这将很大程度上改变LLM的可用范围(当然安全问题依…...
Excel中用VBA实现Outlook发送当前工作簿
Excel中用VBA实现Outlook发送当前工作簿,首先按AltF11打开VBA编辑器,插入模块,并在工具-引用中勾选 Microseft Outlook .0 Object Library(其中为你Microseft Outlook的版本号。 Sub 发送邮件() 保存当前excel ThisWorkbook.Save让excel连接…...
从 ArcMap 迁移到 ArcGIS Pro
许多 ArcMap 用户正在因 ArcGIS Pro 所具有的现代 GIS 桌面工作流优势而向其迁移。 ArcGIS Pro 与其余 ArcGIS 平台紧密集成,使您可以更有效地共享和使用内容。 它还将 2D 和 3D 组合到一个应用程序中,使您可以在同一工程中使用多个地图和多个布局。 Arc…...
WSL2 的安装与运行 Linux 系统
前言 适用于 Linux 的 Windows 子系统 (WSL) 是 Windows 的一项功能,允许开发人员在 Windows 系统上直接安装并使用 Linux 发行版。不用进行任何修改,也无需承担传统虚拟机或双启动设置的开销。 可以将 WSL 看作也是一个虚拟机,但是它更为便…...
业务终端动态分配IP-DHCP技术、DHCP中继技术
一、为什么需要DHCP? 1、许多设备(主机、无线WiFi终端等)需要动态地址的分配; 2、人工手工配置任务繁琐、容易出错,比如:IP地址冲突; 3、网络规模扩大、复杂度提高,网络配置越来越复杂,计算机的位置变化和数量超过可分配IP地址的数量,造成IP地址变法频繁以及IP地址…...
新一代大语言模型 GPT-5 对工作与生活的影响及应对策略
文章目录 📒一、引言 📒二、GPT-5 的发展背景 🚀(一)GPT-4 的表现与特点 🚀(二)GPT-5 的预期进步 📒三、GPT-5 对工作的影响 🚀(一…...
AI基于大模型语言存在的网络安全风险
目的: 随着大语言模型(LLM)各领域的广泛应用,我们迫切需要了解其中潜在的风险和威胁,及时进行有效的防御。 申明: AI技术的普及正当的使用大模型技术带来的便利,切勿使用与非法用途ÿ…...
探索Perl语言:入门学习与实战指南
文章目录 探索Perl语言:入门学习与实战指南一、Perl语言概述二、Perl的安装与配置安装PerlWindowsmacOSLinux 配置Perl 三、基本语法与数据类型标量变量数组哈希 四、控制结构条件语句循环语句 五、子程序与模块子程序模块 六、文件操作与正则表达式文件读取与写入正…...
dp or 数学问题
看一下数据量,只有一千,说明这个不是数学问题 #include<bits/stdc.h> using namespace std;#define int long long const int mo 100000007; int n, s, a, b; const int N 1005;// 2 -3 // 1 3 5 2 -1 // 1 -2 -5 -3 -1 int dp[N][N]; int fun…...
kibana连接elasticsearch(版本8.11.3)
前言 elasticsearch在8版本之后就出现了很大变化,由于kibana版本需要需elasticsearch进行版本对象,kibana连接方式也出现了很大变化。我在这里记录下自己的踩坑记录。 服务部署 本文中的服务都是在docker环境中部署的。其中elasticsearch版本和kibana版…...
基于python的图像去水印
1 代码 import cv2 import numpy as npdef remove_watermark(image_path, output_path):# 读取图片image cv2.imread(image_path)# 转换为灰度图gray cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)# 使用中值滤波去除噪声median_filtered cv2.medianBlur(gray, 5)# 计算图像的梯…...
Linux下Supervisor的安装与配置
软件工程中,守护进程是非常行之有效的方案。能够让我们的一些指令在崩溃之后可以自我重新启动,从而保障业务上的持续使用。 这里就从0开始教大家安装Supervisor。 一,下载安装 安装有好多种方法,直接下载安装包安装,或者yum安装或者pip安装都可以。这次我们选择的是pip…...
使用Pandas读取Excel文件将特定列转成str格式方法汇总
文章目录 读取Excel文件并确保列为字符串类型使用 dtype 参数使用 converters 参数 读取Excel文件的正确拼写示例:读取Excel文件并过滤包含特定值的行详细解释 读取Excel文件并确保列为字符串类型 正确的方法是使用 pd.read_excel 函数,并指定 dtype 或…...
FPGA CFGBVS 管脚接法
说明 新设计了1个KU040 FPGA板子,回来之后接上JTAG FPGA不识别。做如下检查: 1、电源测试点均正常; 2、查看贴片是否有漏焊,检查无异常,设计上NC的才NC; 3、反复检查JTAG接线是否异常,贴片是…...
快速排序及归并排序的实现与排序的稳定性
目录 快速排序 一. 快速排序递归的实现方法 1. 左右指针法 步骤思路 为什么要让end先走? 2. 挖坑法 步骤思路 3. 前后指针法 步骤思路 二. 快速排序的时间和空间复杂度 1. 时间复杂度 2. 空间复杂度 三. 快速排序的优化方法 1. 三数取中优化 2. 小区…...
【系统架构设计】数据库系统(一)
数据库系统(一) 数据库模式与范式数据库的结构与模式数据模型关系代数数据的规范化反规范化 数据库设计事务管理备份与恢复分布式数据库系统数据仓库数据挖掘NoSQL大数据 数据库模式与范式 数据库的结构与模式 数据库技术中采用分级的方法将数据库的结…...
python执行测试用例,allure报乱码且未成功生成报告
allure执行测试用例时显示乱码:‘allure’ �����ڲ����ⲿ���Ҳ���ǿ�&am…...
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中,新增了一个本地验证码接口 /code,使用函数式路由(RouterFunction)和 Hutool 的 Circle…...
AirSim/Cosys-AirSim 游戏开发(四)外部固定位置监控相机
这个博客介绍了如何通过 settings.json 文件添加一个无人机外的 固定位置监控相机,因为在使用过程中发现 Airsim 对外部监控相机的描述模糊,而 Cosys-Airsim 在官方文档中没有提供外部监控相机设置,最后在源码示例中找到了,所以感…...
比较数据迁移后MySQL数据库和OceanBase数据仓库中的表
设计一个MySQL数据库和OceanBase数据仓库的表数据比较的详细程序流程,两张表是相同的结构,都有整型主键id字段,需要每次从数据库分批取得2000条数据,用于比较,比较操作的同时可以再取2000条数据,等上一次比较完成之后,开始比较,直到比较完所有的数据。比较操作需要比较…...
前端高频面试题2:浏览器/计算机网络
本专栏相关链接 前端高频面试题1:HTML/CSS 前端高频面试题2:浏览器/计算机网络 前端高频面试题3:JavaScript 1.什么是强缓存、协商缓存? 强缓存: 当浏览器请求资源时,首先检查本地缓存是否命中。如果命…...
Python常用模块:time、os、shutil与flask初探
一、Flask初探 & PyCharm终端配置 目的: 快速搭建小型Web服务器以提供数据。 工具: 第三方Web框架 Flask (需 pip install flask 安装)。 安装 Flask: 建议: 使用 PyCharm 内置的 Terminal (模拟命令行) 进行安装,避免频繁切换。 PyCharm Terminal 配置建议: 打开 Py…...
2025.6.9总结(利与弊)
凡事都有两面性。在大厂上班也不例外。今天找开发定位问题,从一个接口人不断溯源到另一个 接口人。有时候,不知道是谁的责任填。将工作内容分的很细,每个人负责其中的一小块。我清楚的意识到,自己就是个可以随时替换的螺丝钉&…...
P10909 [蓝桥杯 2024 国 B] 立定跳远
# P10909 [蓝桥杯 2024 国 B] 立定跳远 ## 题目描述 在运动会上,小明从数轴的原点开始向正方向立定跳远。项目设置了 $n$ 个检查点 $a_1, a_2, \cdots , a_n$ 且 $a_i \ge a_{i−1} > 0$。小明必须先后跳跃到每个检查点上且只能跳跃到检查点上。同时࿰…...
深入浅出JavaScript中的ArrayBuffer:二进制数据的“瑞士军刀”
深入浅出JavaScript中的ArrayBuffer:二进制数据的“瑞士军刀” 在JavaScript中,我们经常需要处理文本、数组、对象等数据类型。但当我们需要处理文件上传、图像处理、网络通信等场景时,单纯依赖字符串或数组就显得力不从心了。这时ÿ…...
二维数组 行列混淆区分 js
二维数组定义 行 row:是“横着的一整行” 列 column:是“竖着的一整列” 在 JavaScript 里访问二维数组 grid[i][j] 表示 第i行第j列的元素 let grid [[1, 2, 3], // 第0行[4, 5, 6], // 第1行[7, 8, 9] // 第2行 ];// grid[i][j] 表示 第i行第j列的…...
