当前位置: 首页 > news >正文

手写redux和applyMiddleware中间件react示例

目录

一 核心代码

1.reducer

2.store.js

二 关于context API的使用

1. MyContext

2.  createContext

3. ContextProvider

4. connect

三 组件验证效果

1. Todo

2. TodoList

3.TodoItem

4.TodoInput

5. App组件引入Todo组件


一 核心代码

1.reducer

// 新增列表数据和改变数组数据
// 将业务逻辑拆分到一个单独文件中,方便进行状态管理
import _ from 'lodash';export interface StateProps {id: number;text: string;isFinished: boolean;}export interface ActionProps {type: string;[key: string]: any;}interface IStateObjectProps {pickerArr: StateProps[];filterTag: 'SHOW_ALL'|'SHOW_FINISHED'|'SHOW_NOT_FINISH';dispatch: any;}
const reducer = (state: IStateObjectProps, action: ActionProps) => {console.log(state, action, 'reducer11111');const pickerArr0 = _.get(state, 'pickerArr')||[];switch (_.get(action, 'type')) {case "ADD":return {...state,pickerArr: [...pickerArr0, _.get(action, 'todo')]};case "CHANGESTATUS":const pickerArr = _.map(pickerArr0, (item) => {if (item.id === action.id) {return Object.assign({}, item, { isFinished: !_.get(item, 'isFinished') });}return item;})||[];return {...state,pickerArr,}case 'SET_VISIBILITY_FILTER': const filterTag = action.filterTag;return {...state,filterTag,};default:return state || {};}};export default reducer

2.store.js

import React from 'react';
import reducer from './reducer'// compose执行顺序就是从后往前
const compose = (...funcs) => {if (funcs.length === 0) return arg => argreturn funcs.reduce((v, cur) => (...args) => (v(cur(...args))))
}function applyMiddleware(...args) {// 将中间件们变成一个数组const middlewares = Array.from({ length: args.length }, (_, _key) => args[_key]);return function (createStore) {return function () {const store = createStore.apply(void 0, arguments);const middlewareAPI = {getState: store.getState,dispatch: store.dispatch,};// map遍历中间件, 执行监听器函数, 形成新数组const chain = middlewares.map((middleware) => middleware(middlewareAPI));// 展开中间件,调整执行顺序,并传入store.dispatchconst dispatch = compose(...chain)(store.dispatch);// 返回需要的存储数据(将dispatch合并进store对象)return {...store,dispatch,};};};
}function legacy_createStore(reducer, preloadedState) {let state = preloadedState || null;const listeners = [];const subscribe = (fn) => listeners.push(fn);const getState = () => state;const dispatch = (action) => {const state1 = reducer(state, action);state = state1// 因为是在获取到最新的state的值之后有执行的监听回调, 所以使用store.subscribe可以监听到最新的state的值!!!listeners.forEach((fn) => fn());return state}return { getState, dispatch, subscribe }
}function createStore(reducer, preloadedState, enhancer) {if (typeof preloadedState === 'function') {return preloadedState(legacy_createStore)(reducer)}if (typeof enhancer === 'function') {return enhancer(legacy_createStore)(reducer, preloadedState)}return legacy_createStore(reducer, preloadedState)
}function createThunkMiddleware(extraArgument) {return ({ dispatch, getState }) => next => action => {if (typeof action === 'function') {return action(dispatch, getState, extraArgument)}return next(action)}
}const thunk = createThunkMiddleware('xxxxxx');
const store = applyMiddleware(thunk)(createStore)(reducer);
// 或者
// const store = createStore(reducer, applyMiddleware(thunk));
// 或者
// const store = createStore(reducer);console.log(store, 'oldState======')
store.subscribe(() => {const newState = store.getState()// 数据可能变化,需要监听最新的console.log(newState, 'newState====');
})export default store;

二 关于context API的使用

1. MyContext

import React from "react";const MyContext = React.createContext({});export default MyContext

2.  createContext

import React, {ReactNode, memo} from "react";// 已经使用了React.createContext, 这个可以忽略, 只是为了展示createContext功能的简单代码
const createContext = ({}) => {let value = {};const Provider = memo((props: {children: ReactNode;value:{ dispatch: (arg1:any)=>void; getState:() => any;};}) => {value = props.value;return <>{props.children}</>;});const Consumer = memo(({ children }: { children: any }) => {return <>{typeof children === "function" ? children(value) : children}</>;});return { Provider, Consumer };
};export default createContext;

3. ContextProvider

import React, { useState } from "react";
import MyContext from "./MyContext";
import _ from "lodash";
import store from "./store";const useReducer = (state0, dispatch0) => {const [state, dispatch] = useState(state0);const dispatch1 = (action) => {dispatch0(action);dispatch(store.getState());};return [state, dispatch1]
}// 父组件
const ContextProvider = ({ children }) => {const [state, dispatch] = useReducer(store.getState(), store.dispatch);return <MyContext.Provider value={{getState: () => state,dispatch}}>{children}</MyContext.Provider>;
};export default ContextProvider;

4. connect

import React from "react";
import MyContext from "./MyContext";
import _ from "lodash";// 模拟react-redux的 connect高阶函数
const connect = (mapStateToProps, mapDispatchToProps) => {return (Component) => (props) =>wrapper(Component, { mapStateToProps, mapDispatchToProps, ...props });
};const wrapper = (Comp, props) => {const { mapStateToProps, mapDispatchToProps, ...rest } = props;return (<MyContext.Consumer>{(store) => {const dispatchs = mapDispatchToProps(_.get(store, 'dispatch'));let states1 = mapStateToProps(_.get(store, 'getState') ? _.get(store, 'getState')(): {});return <Comp {...{ ...states1, ...dispatchs, ...rest }} />;}}</MyContext.Consumer>);
};export default connect;

三 组件验证效果

1. Todo

import React from "react";
import TodoInput from "./TodoInput";
import TodoList from "./TodoList";
import ContextProvider from "./ContextProvider";// 父组件
const Todo = () => {return (<ContextProvider><TodoInput /><TodoList /></ContextProvider>);
};
export default Todo;

2. TodoList

import React, { useEffect } from "react";
import TodoItem from "./TodoItem";
import _ from "lodash";
import connect from "./connect";
import { mapStateTotProps } from "./mapStateToProps";
import { mapDispatchToProps } from "./mapDispatchToProps";
import styles from './TodoList.scss'const TodoList = (props) => {const { todoList } = props;console.log(styles, 'TodoList-styles', props)return (<><p className={styles.title}>checckbox-list: </p><div className="todo-list">{_.map(todoList, (item) => (<TodoItem key={_.get(item, "id")} todo={item || {}} />))}</div><hr /></>);
};export default connect(mapStateTotProps, mapDispatchToProps)(TodoList);

3.TodoItem

import _ from 'lodash';
import React from "react";
import connect from './connect';
import { mapStateTotProps } from "./mapStateToProps";
import { mapDispatchToProps } from "./mapDispatchToProps";// 孙子组件
const TodoItem = (props: any) => {const { todo, changeTodo } = props;// 改变事项状态const handleChange = () => {changeTodo(_.get(todo, 'id'));}return (<div className="todo-item"><input type="checkbox" checked={todo.isFinished} onChange={handleChange} /><span style={{ textDecoration: _.get(todo, 'isFinished') ? 'line-through' : 'none' }}>{todo.text}</span></div>)
}export default connect(mapStateTotProps, mapDispatchToProps)(TodoItem);

4.TodoInput

import React, { useState } from "react";
import connect from './connect';
import { mapStateTotProps } from "./mapStateToProps";
import { mapDispatchToProps } from "./mapDispatchToProps";
import styles from './TodoInput.scss'// 子组件
const TodoInput = (props) => {// console.log(styles, 'styles', props)const [text, setText] = useState("");const {addTodo,showAll,showFinished,showNotFinish,} = props;const handleChangeText = (e: React.ChangeEvent) => {setText((e.target as HTMLInputElement).value);};const handleAddTodo = () => {if (!text) return;addTodo({id: new Date().getTime(),text: text,isFinished: false,});setText("");};return (<div className={styles["todo-input"]}><inputtype="text"placeholder="请输入代办事项"onChange={handleChangeText}value={text}className="aaa"/><button className={styles.btn} onClick={handleAddTodo}>+添加</button><button className={styles.btn} onClick={showAll}>show all</button><button className={styles.btn} onClick={showFinished}>show finished</button><button className={styles.btn} onClick={showNotFinish}>show not finish</button></div>);
};export default connect(mapStateTotProps, mapDispatchToProps)(TodoInput);

5. App组件引入Todo组件

相关文章:

手写redux和applyMiddleware中间件react示例

目录 一 核心代码 1.reducer 2.store.js 二 关于context API的使用 1. MyContext 2. createContext 3. ContextProvider 4. connect 三 组件验证效果 1. Todo 2. TodoList 3.TodoItem 4.TodoInput 5. App组件引入Todo组件 一 核心代码 1.reducer // 新增列表数…...

MATLAB R2024a 主要更新内容

系列文章目录 前言 一、主要更新 计算机视觉工具箱 —— 为二维和三维视觉任务设计算法、标注数据并生成代码。深度学习工具箱 —— 支持变换器等架构&#xff1b;导入并共同模拟 PyTorch 和 TensorFlow 模型。仪器控制工具箱 —— 使用仪器资源管理器应用程序管理带有 IVI 和…...

4.1.CVAT——目标检测的标注详细步骤

文章目录 1. 进入任务1. 创建任务2. 已创建的task3. 进入标注界面 2. 选择标注类型2.1 选择标注类型2.2 进行标注2.3 遮挡 2.快捷键3.导出标注结果 1. 进入任务 登录后会看到如下图界面&#xff0c;CVAT的标注最小单位是Task&#xff0c;每个Task为一个标注任务。点击Task按钮…...

图论-算法题

797. 所有可能的路径 题目: 给你一个有 n 个节点的 有向无环图&#xff08;DAG&#xff09;&#xff0c;请你找出所有从节点 0 到节点 n-1 的路径并输出&#xff08;不要求按特定顺序&#xff09; graph[i] 是一个从节点 i 可以访问的所有节点的列表&#xff08;即从节点 i …...

onnx 1.16 doc学习笔记七:python API一览

onnx作为一个通用格式&#xff0c;很少有中文教程&#xff0c;因此开一篇文章对onnx 1.16文档进行翻译与进一步解释&#xff0c; onnx 1.16官方文档&#xff1a;https://onnx.ai/onnx/intro/index.html](https://onnx.ai/onnx/intro/index.html)&#xff0c; 如果觉得有收获&am…...

LACP——链路聚合控制协议

LACP——链路聚合控制协议 什么是LACP&#xff1f; LACP&#xff08;Link Aggregation Control Protocol&#xff0c;链路聚合控制协议&#xff09;是一种基于IEEE802.3ad标准的实现链路动态聚合与解聚合的协议&#xff0c;它是链路聚合中常用的一种协议。 链路聚合组中启用了…...

终端启动jupyter notebook更换端口

一、问题描述 如果尝试在端口 8889 上启动 Jupyter Notebook 但最终启动在了 8890 端口&#xff0c;这通常意味着 8889 端口已经被占用。要解决这个问题&#xff0c;可以尝试以下几种方法来关闭占用 8889 端口的进程。 1. 查找并终止占用端口的进程 首先&#xff0c;需要找出…...

IT发布管理,轻松部署软件

我们带来了一项令人振奋的好消息&#xff0c;可有效缓解构建的质量相对劣质和发布的速度相对缓慢。 ManageEngine卓豪推出了ServiceDesk Plus MSP中的IT发布管理&#xff0c;配备了可视化的工作流程&#xff0c;这是PSA-ITSM解决方案的一部分。有了这个新功能&#xff0c;您可以…...

2024国际生物发酵展览会独家解读-力诺天晟科技

参展企业介绍 北京力诺天晟科技有限公司&#xff0c;专业致力于智能仪器仪表制造&#xff0c;工业自动控制系统用传感器、变送器的研发、设计、销售和服务。 公司坐落于首都北京行政副中心-通州区&#xff0c;下设生产子公司位于河北香河经济开发区&#xff0c;厂房面积 300…...

YOLOv9尝鲜测试五分钟极简配置

pip安装python包&#xff1a; pip install yolov9pip在https://github.com/WongKinYiu/yolov9/tree/main中下载好权重文件yolov9-c.pt。 运行下面代码&#xff1a; import yolov9model yolov9.load("yolov9-c.pt", device"cpu") # load pretrained or c…...

消息中间件篇之Kafka-消息不丢失

一、 正常工作流程 生产者发送消息到kafka集群&#xff0c;然后由集群发送到消费者。 但是可能中途会出现消息的丢失。下面是解决方案。 二、 生产者发送消息到Brocker丢失 1. 设置异步发送 //同步发送RecordMetadata recordMetadata kafkaProducer.send(record).get();//异…...

Rust使用calamine读取excel文件,Rust使用rust_xlsxwriter写入excel文件

Rust使用calamine读取已存在的test.xlsx文件全部数据&#xff0c;还读取指定单元格数据&#xff1b;Rust使用rust_xlsxwriter创建新的output.xlsx文件&#xff0c;并写入数据到指定单元格&#xff0c;然后再保存工作簿。 Cargo.toml main.rs /*rust读取excel文件*/ use cala…...

中文文本分类(pytorch 实现)

import torch import torch.nn as nn import torchvision from torchvision import transforms, datasets import os, PIL, pathlib, warningswarnings.filterwarnings("ignore") # 忽略警告信息# win10系统 device torch.device("cuda" if torch.cuda.i…...

【每日前端面经】2023-02-27

题目来源: 牛客 CSS盒模型 CSS中的盒子包括margin|border|padding|content四个部分&#xff0c;对于标准盒子模型&#xff08;content-box&#xff09;的widthcontent&#xff0c;但是对于IE盒子模型&#xff08;border-box&#xff09;的widthcontentborder2padding2 CSS选…...

springboot + easyRules 搭建规则引擎服务

依赖 <dependency><groupId>org.jeasy</groupId><artifactId>easy-rules-core</artifactId><version>4.0.0</version></dependency><dependency><groupId>org.jeasy</groupId><artifactId>easy-rules…...

Mac电脑配置环境变量

1.打开配置文件bash_profile open -e .bash_profile 2.如果没有创建过.bash_profile&#xff0c;则先需要创建 touch .bash_profile 3.输入你要配置的环境变量 #Setting PATH for Android ADB Tools export ANDROID_HOME/Users/xxx/android export PATH${PATH}:${ANDROID_HOME}…...

Windows系统x86机器安装(麒麟、统信)ARM系统详细教程

本次介绍在window系统x86机器上安装国产系统 arm 系统的详细教程。 注&#xff1a;ubuntu 的arm系统安装是一样的流程。 1.安装环境准备。 首先&#xff0c;你得有台电脑&#xff0c;配置别太差&#xff0c;至少4核8G内存&#xff0c;安装window10或者11都行&#xff08;为啥…...

消息中间件篇之RabbitMQ-高可用机制

一、怎么保证高可用性 在生产环境下&#xff0c;使用集群来保证高可用性&#xff0c;一般我们采用普通集群、镜像集群、仲裁队列。 二、普通集群 普通集群&#xff0c;或者叫标准集群&#xff08;classic cluster&#xff09;&#xff0c;具备下列特征&#xff1a; 1. 会在集…...

express+mysql+vue,从零搭建一个商城管理系统5--用户注册

提示&#xff1a;学习express&#xff0c;搭建管理系统 文章目录 前言一、新建user表二、安装bcryptjs、MD5、body-parser三、修改config/db.js四、新建config/bcrypt.js五、新建models文件夹和models/user.js五、index.js引入使用body-parser六、修改routes/user.js七、启动项…...

canvas水波纹效果,jquery鼠标水波纹插件

canvas水波纹效果&#xff0c;jquery鼠标水波纹插件 效果展示 jQuery水波纹效果&#xff0c;canvas水波纹插件 HTML代码片段 <div class"scroll04wrap"><h3>发展历程</h3><div class"scroll04"><p>不要回头&#xff0c;一…...

低查重AI教材写作秘籍:必备AI工具推荐,快速完成教材编写!

许多教材编写者常常感到遗憾&#xff0c;虽然他们对正文内容进行了精心打磨&#xff0c;但配套资源的缺失却使得整体教学效果受到影响。设计课后练习时往往缺乏创新的思路&#xff0c;制作直观的教学课件又因为技术能力不足而举步维艰&#xff0c;而对于案例的深入解析则因为时…...

学会给AI搭系统,才是2026年最值钱的技能!收藏这份保姆级指南

文章对比了学习AI工具和使用AI系统两种方式&#xff0c;强调后者更具有长远价值。通过实例展示&#xff0c;搭建AI系统可以极大提高效率&#xff0c;且这种能力比单纯会使用AI工具更难掌握&#xff0c;因此更值得学习。文章提出“驾驭工程”概念&#xff0c;并给出普通人学习搭…...

Ollama环境变量全解析:从外网访问到模型路径设置,一篇搞定所有配置

Ollama环境变量全解析&#xff1a;从外网访问到模型路径设置&#xff0c;一篇搞定所有配置 最近在部署Ollama服务时&#xff0c;我发现很多开发者对环境变量的配置存在困惑。特别是在需要外网访问、自定义模型路径或优化性能时&#xff0c;正确的环境变量设置能节省大量调试时间…...

FireRed-OCR Studio效果展示:带页眉页脚文档区域智能过滤

FireRed-OCR Studio效果展示&#xff1a;带页眉页脚文档区域智能过滤 1. 工业级文档解析新标杆 FireRed-OCR Studio代表了当前文档解析技术的最高水平。这款基于Qwen3-VL模型深度优化的工具&#xff0c;不仅能准确识别文字内容&#xff0c;更能智能理解文档的完整结构布局。在…...

告别objdump!用Python的pwntools一键生成汇编对应的hex机器码(附Mac/Linux安装避坑)

告别objdump&#xff01;用Python的pwntools一键生成汇编对应的hex机器码&#xff08;附Mac/Linux安装避坑&#xff09; 在二进制安全研究和CTF竞赛中&#xff0c;快速将汇编指令转换为机器码是每个从业者的基本功。传统方法依赖gcc或nasm配合objdump工具链&#xff0c;不仅步骤…...

3分钟从视频中智能提取PPT演示文稿:告别繁琐截图的终极方案

3分钟从视频中智能提取PPT演示文稿&#xff1a;告别繁琐截图的终极方案 【免费下载链接】extract-video-ppt extract the ppt in the video 项目地址: https://gitcode.com/gh_mirrors/ex/extract-video-ppt 你是否曾为从视频课程、会议录像中手动截图PPT而烦恼&#xf…...

别再傻傻拼手速了!用Java实现微信/支付宝那种‘拼手气红包’的公平算法(附完整代码)

揭秘微信红包背后的公平算法&#xff1a;用Java实现拼手气红包系统 每次在群里抢红包时&#xff0c;你是否好奇过为什么有人能抢到大额红包&#xff0c;而有人只能拿到几分钱&#xff1f;这背后其实是一套精心设计的算法在运作。本文将带你深入理解主流支付平台的拼手气红包实…...

Android MediaRecorder独占锁揭秘:为什么你的录音和系统通话录音会互相打架?

Android音频独占锁机制&#xff1a;破解MediaRecorder与系统通话录音的资源争夺战 当你在开发一款需要后台录音的Android应用时&#xff0c;是否遇到过这样的尴尬场景&#xff1a;用户接听电话时&#xff0c;你的应用正在录音&#xff0c;结果系统通话录音功能要么完全失效&…...

【实战指南】从零到精通:用C打造你的Switch模拟器体验

【实战指南】从零到精通&#xff1a;用C#打造你的Switch模拟器体验 【免费下载链接】Ryujinx 用 C# 编写的实验性 Nintendo Switch 模拟器 项目地址: https://gitcode.com/GitHub_Trending/ry/Ryujinx 想象一下这样的场景&#xff1a;你刚刚下载了最新的Switch游戏&…...

Dify API网关调试不靠猜:用OpenTelemetry+Prometheus构建可观测性闭环(附可复用SLO告警模板)

第一章&#xff1a;Dify API 网关调试不靠猜&#xff1a;用OpenTelemetryPrometheus构建可观测性闭环&#xff08;附可复用SLO告警模板&#xff09;为什么传统日志排查在Dify网关场景中失效 Dify 的异步任务流&#xff08;如 LLM 推理、RAG 检索、工具调用&#xff09;导致请求…...