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));
            // 重新组装后的 dispatch
            dispatch = 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 = defaultState
        defaultState = 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://www.mfbz.cn/a/802445.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

数据融合工具(10)线重叠检查修复

一、需求背景 先明确一下“线重叠”的定义。 ArcGIS拓扑工具集中的拓扑规则: 不能自重叠(线) —线要素不得与自身重叠。这些线要素可以交叉或接触但不得有重合的线段。此规则适用于街道等线段可能接触闭合线的要素,但同一街道不得…

深入探讨极限编程(XP):技术实践与频繁发布的艺术

目录 前言1. 极限编程的核心原则1.1 沟通1.2 简单1.3 反馈1.4 勇气1.5 尊重 2. 关键实践2.1 结对编程2.1.1 提高代码质量2.1.2 促进知识共享2.1.3 增强团队协作 2.2 测试驱动开发(TDD)2.2.1 提升代码可靠性2.2.2 提高代码可维护性2.2.3 鼓励良好设计 2.3…

判断点与圆的位置关系(c++)

可以通过创建两个类来解决问题 &#xff1a; 代码&#xff1a; #include<iostream> using namespace std;class Point { public:void setX(int x){m_X x;}int getX(){return m_X;}void setY(int y){m_Y y;}int getY(){return m_Y;}private:int m_X;int m_Y;};class C…

【系统架构设计师】十一、系统架构设计(中间件|典型应用架构)

目录 九、中间件 9.1 基础概念 9.2 中间件分类 十、典型应用架构 10.1 J2EE和四层结构 10.2 JSPServletJavaBeanDAO 10.3 .NET和J2EE之争 往期推荐 历年真题练习 九、中间件 之前总提到中间件&#xff0c;那么中间件到底是什么&#xff1f;在系统架构中又扮演者什么角…

Spring与设计模式实战之策略模式

Spring与设计模式实战之策略模式 引言 在现代软件开发中&#xff0c;设计模式是解决常见设计问题的有效工具。它们提供了经过验证的解决方案&#xff0c;帮助开发人员构建灵活、可扩展和可维护的系统。本文将探讨策略模式在Spring框架中的应用&#xff0c;并通过实际例子展示…

C++ | Leetcode C++题解之第240题搜索二维矩阵II

题目&#xff1a; 题解&#xff1a; class Solution { public:bool searchMatrix(vector<vector<int>>& matrix, int target) {int m matrix.size(), n matrix[0].size();int x 0, y n - 1;while (x < m && y > 0) {if (matrix[x][y] targ…

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

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

AI自动生成PPT哪个软件好?高效制作PPT优选这4个

7.15初伏的到来&#xff0c;也宣告三伏天的酷热正式拉开序幕~在这个传统的节气里&#xff0c;人们以各种方式避暑纳凉&#xff0c;享受夏日的悠闲时光。 而除了传统的避暑活动&#xff0c;我们还可以用一种新颖的方式记录和分享这份夏日的清凉——那就是通过PPT的方式将这一传…

【Linux】权限管理与相关指令

文章目录 1.权限、文件权限、用户文件权限的理解以及对应八进制数值表示、设置目录为粘滞位文件类型 2.权限相关的常用指令susudochmodchownchgrpumaskwhoamifile 1.权限、文件权限、用户 通过一定条件&#xff0c;拦住一部分人&#xff0c;给另一部分权利来访问资源&#xff0…

Amazon EC2 部署Ollama + webUI

最近和同事闲聊&#xff0c;我们能不能内网自己部署一个LLM&#xff0c;于是便有了Ollama webUI的尝试 对于Linux&#xff0c;使用一行命令即可 curl -fsSL https://ollama.com/install.sh | shollama --help Large language model runnerUsage:ollam…

【Dison夏令营 Day 21】用Python编写绘图

绘画 - 在屏幕上绘制线条和形状。单击标记形状的起点&#xff0c;再次单击标记形状的终点。可使用键盘选择不同的形状和颜色。 """Paint, for drawing shapes.Exercises1. Add a color. 2. Complete circle. 3. Complete rectangle. 4. Complete triangle. 5. A…

大厂面试官问我:为什么Redis的rehash采用渐进式,而Java的hashmap是一次性rehash?【后端八股文十二:Redis hash八股文合集】

本文为【Redis hash八股文合集】初版&#xff0c;后续还会进行优化更新&#xff0c;欢迎大家关注交流~ hello hello~ &#xff0c;这里是绝命Coding——老白~&#x1f496;&#x1f496; &#xff0c;欢迎大家点赞&#x1f973;&#x1f973;关注&#x1f4a5;&#x1f4a5;收藏…

PostgreSQL使用(二)

说明&#xff1a;本文介绍PostgreSQL的DML语言&#xff1b; 插入数据 -- 1.全字段插入&#xff0c;字段名可以省略 insert into tb_student values (1, 张三, 1990-01-01, 88.88);-- 2.部分字段插入&#xff0c;字段名必须写全 insert into tb_student (id, name) values (2,…

Windows 11几个常用的快捷键

WinQ/WinS&#xff1a;快速打开一键搜索 WinCtrlD&#xff1a;快速新建虚拟桌面。可以通过四根手指在触摸板划动进行切换&#xff0c;也可以通过WinTAB进行虚拟桌面切换 WinTAB&#xff1a;虚拟桌面切换 WinA&#xff1a;打开快速设置面板 WinD&#xff1a;快速切换到桌面&a…

springboot+vue+mybatis鲜花管理系统+PPT+论文+讲解+售后

随着科学技术的飞速发展&#xff0c;社会的方方面面、各行各业都在努力与现代的先进技术接轨&#xff0c;通过科技手段来提高自身的优势&#xff0c;鲜花管理系统当然也不能排除在外。鲜花管理系统是以实际运用为开发背景&#xff0c;运用软件工程开发方法&#xff0c;采用SSM技…

如何建设微模块数据中心机房

建设微模块数据中心机房涉及多个步骤和技术要点&#xff0c;以下是基于现代技术和行业最佳实践的建设流程概览&#xff1a; 1. 需求分析与规划 确定需求&#xff1a;分析业务需求&#xff0c;包括IT容量、电力、冷却和空间需求。 选址评估&#xff1a;选择靠近关键基础设施的位…

秋招突击——7/16——复习{滑动窗口——无重复最长子串}——新作{相交链表,环形链表,滑动窗口——找到字符串中所有字母异位词}

文章目录 引言模板——滑动窗口/双指针复习无重复最长子串复习实现 新作相交链表个人实现参考实现 环形链表个人实现参考实现——快慢指针 找到字符串中所有字母的异位词个人实现参考实现 总结 引言 今天本来想完成指标的&#xff0c;但是做着做着&#xff0c;发现大部分做的比…

Day07-ES集群加密,kibana的RBAC实战,zookeeper集群搭建,zookeeper基本管理及kafka单点部署实战

Day07-ES集群加密&#xff0c;kibana的RBAC实战&#xff0c;zookeeper集群搭建&#xff0c;zookeeper基本管理及kafka单点部署实战 0、昨日内容回顾:1、基于nginx的反向代理控制访问kibana2、配置ES集群TSL认证:3、配置kibana连接ES集群4、配置filebeat连接ES集群5、配置logsta…

用go实现限流算法

文章目录 固定窗口优缺点&#xff1a;适用场景&#xff1a;总结&#xff1a; 滑动窗口优缺点&#xff1a;适用场景&#xff1a;总结&#xff1a; 漏桶限流器优缺点&#xff1a;适用场景&#xff1a;总结&#xff1a; 令牌桶优缺点&#xff1a;适用场景&#xff1a;总结&#xf…

保障低压设备安全!中国星坤连接器精密工艺解析!

在现代电子设备中&#xff0c;连接器扮演着至关重要的角色&#xff0c;它们是电子系统之间沟通的桥梁。随着技术的发展&#xff0c;对连接器的需求也在不断提升&#xff0c;特别是在低电压应用领域。中国星坤最新推出的低压连接器&#xff0c;以其精密性和安全性&#xff0c;为…