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(".");
}

以上。

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

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

相关文章

【精品资料】大数据可视化平台数据治理方案(626页WORD)

引言:大数据可视化平台的数据治理方案是一个综合性的策略,旨在确保大数据的质量、安全性、可访问性和合规性,从而支持高效的数据分析和可视化过程。 方案介绍: 大数据可视化平台的数据治理方案是一个综合性的策略,旨在…

Nginx源码windows下编译

参考:Nginx Windows下编译和安装_window 如果编译安装nginx-CSDN博客 参考: Building nginx on the Win32 platform with Visual C 1.安装MSYS2 参考 MSYS2 下 installation 内容下载安装即可。 2.下载依赖库: PCRE:Download PCRE ​ …

在设计电气系统时,电气工程师需要考虑哪些关键因素?

在设计电气系统时,电气工程师需要考虑多个关键因素,以确保系统的安全性、可靠性、效率和经济性。我收集归类了一份plc学习包,对于新手而言简直不要太棒,里面包括了新手各个时期的学习方向编程教学、问题视频讲解、毕设800套和语言…

三球交汇定理计算标签位置(UWB)

目录 题目三球交汇定理计算过程代码实现说明 题目 假设标签坐标为(x0,y0),基站ABCD的坐标分别为(x1,y1)(x2,y2)(x3,y3)(x4,y4),基站坐标已知,如何计算标签坐标? 三球交汇定理 三球交汇定位( Three-ball intersection…

SAP PP学习笔记28 - 生产订单的收货及品质管理

上一章讲了生产订单的很多概念,比如确认(报工)以及报工的各种形式,反冲,自动入库等。 SAP PP学习笔记27 - Confirmation(报工/确认)(CO11,CO11N,CO15,CO12),…

【排序算法】1.冒泡排序-C语言实现

冒泡排序(Bubble Sort)是最简单和最通用的排序方法,其基本思想是:在待排序的一组数中,将相邻的两个数进行比较,若前面的数比后面的数大就交换两数,否则不交换;如此下去,直…

ospf复习综合小实验

实验要求: 1,R4为ISP,其上只能配置IP地址;R4与其他所有直连设备间均使用公有IP 2,R3-R5/6/7为MGRE环境,R3为中心站点; 3,整个OSPF环境IP基于172.16.0.0/16划分; 4&#…

Pytorch学习笔记day1—— 安装教程

这里写自定义目录标题 Pytorch安装方式 工作需要,最近开始搞一点AI的事情。但是这个国产的AI框架,实话说对初学者不太友好 https://www.mindspore.cn/ 比如说它不支持win下的CUDA,可是我手里只有3070Ti和4060也不太可能自己去买昇腾就有点绷不…

读人工智能全传15意向立场

1. 物理立场 1.1. 可以解释一个实体行为 1.2. 在物理立场中,我们使用自然法则(物理、化学等)来预测系统的行为结果 1.3. 虽然物理立场在解释这种行为的时候非常有效,但无法应用于理解或者预测人类行为 1.3.1. …

0601大学物理电磁篇 静电场中的导体和电介质

静电场中的导体和电介质01 6-1静电场中的导体 6-1静电场中的导体

【Java--数据结构】二叉树

欢迎关注个人主页:逸狼 创造不易,可以点点赞吗~ 如有错误,欢迎指出~ 树结构 树是一种非线性的数据结构,它是由n(n>0)个有限结点组成一个具有层次关系的集合 注意:树形结构中,子…

Qt实现IP地址输入框-自定义控件

在 许多应用程序中,我们经常需要使用IP地址。为了方便用户输入和处理,一个好的解决方案是使用自定义控件。本示例代码使用Qt编写一个名为“IPAddress”的自定义控件来实现IP地址的输入功能。通过使用此控件,用户可以方便地输入和处理IP地址。…

【中项第三版】系统集成项目管理工程师 | 第 5 章 软件工程② | 5.4 - 5.8

前言 第 5 章对应的内容选择题和案例分析都会进行考查,这一章节属于技术的内容,学习要以教材为准。 目录 5.4 软件实现 5.4.1 软件配置管理 5.4.2 软件编码 5.4.3 软件测试 5.5 部署交付 5.5.1 软件部署 5.5.2 软件交付 5.5.3 持续交付 5.5.4…

全新升级!联想Windows 10 22H2专业版,一键下载!

联想Windows 10 22H2专业版系统适用于联想笔记本、台式机安装使用,全新优化升级,运作更流畅更稳定,丰富多样的系统功能,轻松满足用户日常学习、工作的使用需求。同时,该版本系统能够正常更新补丁,您也可以手…

useState函数

seState是一个react Hook(函数),它允许我们像组件添加一个状态变量,从而控制影响组件的渲染结果 数据驱动试图 本质:和普通JS变量不同的是,状态变量一旦发生变化组件的视图UI也会随着变化(数据驱动试图) 使用 修改状态 注意&am…

LabVIEW异步和同步通信详细分析及比较

1. 基本原理 异步通信: 原理:异步通信(Asynchronous Communication)是一种数据传输方式,其中数据发送和接收操作在独立的时间进行,不需要在特定时刻对齐。发送方在任何时刻可以发送数据,而接收…

Java猿社区—理解Java中的字符串比较机制

Java中的字符串比较是一个经典且常见的问题,尤其是在面试中。本文将详细探讨通过三种不同方式创建的字符串对象之间的比较机制,并扩展相关的技术问题,帮助读者深入理解Java的字符串处理。 文章目录 1. Java中的字符串对象创建方式2. 和equals…

在AWS创建一台Windows主机并登录

正文共:1111 字 21 图,预估阅读时间:1 分钟 因为之前微软云Azure免费,我们还做了简单的测试(白嫖党618福利!来Azure领200美刀!外加云主机免费用一年!);并且通…

睡前故事—绿色科技的未来:可持续发展的梦幻故事

欢迎来到《Bedtime Stories Time》。这是一个我们倾听、放松、并逐渐入睡的播客。感谢你收听并支持我们,希望你能将这个播客作为你睡前例行活动的一部分。今晚我们将讲述绿色科技的未来:可持续发展的梦幻故事的故事。一个宁静的夜晚,希望你现…

【15】Android基础知识之Window(一)

概述 这篇文章纠结了很久,在想需要怎么写?因为window有关的篇幅,如果需要讲起来那可太多了。从层级,或是从关联,总之不是很好开口。这次也下定决心,决定从浅入深的讲讲window这个东西。 Window Window是…