手写redux的connect方法, 使用了subscribe获取最新数据

news2025/3/1 2:43:48

一. 公共方法文件

1. connect文件

import React, { useState } 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 dispatch = _.get(store, 'dispatch');
      const dispatchs = mapDispatchToProps(dispatch);
      // store.subscribe监听store.getState()获取最新的值
      const [states, setStates] = useState({})
      store.subscribe(() => {
        const state1 = store.getState();
        setStates(mapStateToProps(state1))
      });
      
      return <Comp {...{...states, ...dispatchs, ...rest}}/>;
    }}
  </MyContext.Consumer>
  
}

export default connect;

2. MyContext文件:

import React from "react";
import createContext from './createContext'

const MyContext = createContext({});

export default MyContext

3. createContext文件:

import React from "react";

const createContext = ({}) => {
  let value = {};

  const Provider = (props) => {
    value = props.value;

    return <>{props.children}</>;
  };

  const Consumer = ({ children }: { children: any }) => {
    return <>{typeof children === "function" ? children(value) : children}</>;
  };

  return { Provider, Consumer };
};

export default createContext;

4. 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, 'reducer');
    const pickerArr0 = _.get(state, 'pickerArr')||[];
    switch (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

5. mapStateToProps文件:

import React from "react";
import _ from "lodash";
import store from "./store";

 // 不同类型的 todo 列表
 const getVisibleTodos = (todos, filter) => {
  switch (filter) {
    case "SHOW_ALL": // 全部显示
      return todos;
    case "SHOW_FINISHED":
      return todos.filter((t) => t.isFinished);
    case "SHOW_NOT_FINISH":
      return todos.filter((t) => !t.isFinished);
    default:
      return todos;
  }
};

export const mapStateTotProps = (state) => {
  // console.log(state, 'mapStateTotProps', store)
  return {
    todoList: getVisibleTodos(_.get(state, 'pickerArr')||[], _.get(state, 'filterTag'))|| [],
  }
}

6. mapDispatchToProps文件

import React from "react";
import _ from "lodash";
import { StateProps } from "./reducer";

export const mapDispatchToProps = (dispatch) => {
  // console.log(dispatch, 'mapDispatchToProps============')
  // 筛选todo列表
  const onFilterTodoList = (filterTag) => {
    dispatch({ type: 'SET_VISIBILITY_FILTER', filterTag, });
  };
  const changeTodo = (id: number) => {
    dispatch({ type: "CHANGESTATUS", id: id });
  };
  // 添加todo
  const addTodo = (todo: StateProps) => {
    dispatch({ type: "ADD", todo });
  };
  const showAll = () => onFilterTodoList("SHOW_ALL");
  const showFinished = () => onFilterTodoList("SHOW_FINISHED");
  const showNotFinish = () => onFilterTodoList("SHOW_NOT_FINISH");
  
  return {
    changeTodo,
    addTodo,
    showAll,
    showFinished,
    showNotFinish,
  };
}

由mapStateToProps文件和mapDispatchToProps文件可知, 我们需要想办法获取最新的state, 和通用的dispatch方法, 也就是以下所说的store文件里面的默认导出对象:

7. store文件:

import React from 'react';
import reducer from './reducer'

function createStore(reducer) {
    let state = 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
    }
    // dispatch({}) 
    return { getState, dispatch, subscribe, reducer }
}

const store = createStore(reducer)

console.log(store.getState(), 'oldState======')
store.subscribe(() => {
    const newState = store.getState() 
    // 数据可能变化,需要监听最新的
    console.log(newState, 'newState====');
})


export default store;

8. ContextProvider组件:

import React from "react";
import MyContext from "./MyContext";
import store from "./store";
import _ from "lodash";

// 父组件
const ContextProvider = ({ children }) => {
  return <MyContext.Provider value={store}>{children}</MyContext.Provider>;
};

export default ContextProvider;

二. 使用公共文件

1.  TodoInput组件

import React, { useState } from "react";
import "./TodoInput.scss";
import connect from './connect';
import { mapStateTotProps } from "./mapStateToProps";
import { mapDispatchToProps } from "./mapDispatchToProps";

// 子组件
const TodoInput = (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="todo-input">
      <input
        type="text"
        placeholder="请输入代办事项"
        onChange={handleChangeText}
        value={text}
      />
      <button onClick={handleAddTodo}>+添加</button>
      <button onClick={showAll}>show all</button>
      <button onClick={showFinished}>show finished</button>
      <button onClick={showNotFinish}>show not finish</button>
    </div>
  );
};

export default connect(mapStateTotProps, mapDispatchToProps)(TodoInput);

2. TodoList组件

import React from "react";
import TodoItem from "./TodoItem";
import _ from "lodash";
import connect from "./connect";
import { mapStateTotProps } from "./mapStateToProps";
import { mapDispatchToProps } from "./mapDispatchToProps";

const TodoList = (props) => {
  const { todoList } = props;

  return (
    <>
      <p>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. Todo组件:

import React from "react";
import TodoInput from "./TodoInput";
import TodoList from "./TodoList";

// 父组件
const Todo = () => {
  return (
    <>
      <TodoInput />
      <TodoList />
    </>
  );
};
export default Todo;

5. App组件使用ContextProvider包裹Todo组件

import React from "react";
import Todo from './mockConnectProvider/Todo'
import ContextProvider from './mockConnectProvider/ContextProvider'

const App: React.FC = () => {
  return (
    <ContextProvider>
      <Todo />
    </ContextProvider>
  );
};

export default App;

效果图如下:

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

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

相关文章

都2023了!别再问我,UI自动化测试怎么做了……

本文关键词&#xff1a;移动端UI自动化思路 大家好&#xff0c;我是老司机。之前测试交流群里有同学问“有没有自动化测试在工作中的案例可以分享“&#xff0c;有是有的。 今天我会详细的描述一个【UI自动化实战在实际工作中的应用】&#xff0c;这是之前贝壳找房我们团队做…

vue单向绑定和双向绑定

一、单向绑定就是&#xff1a;修改视图&#xff0c;数据不变&#xff1b;修改数据&#xff08;app.name"1234"&#xff09;&#xff0c;视图会变 二、双向绑定&#xff1a;修改视图&#xff0c;数据会变&#xff1b;修改数据&#xff0c;视图会变 demo&#xff1a; …

屋顶太阳能光伏系统的性能分析指标研究

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

基于晶体结构优化的BP神经网络(分类应用) - 附代码

基于晶体结构优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码 文章目录 基于晶体结构优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码1.鸢尾花iris数据介绍2.数据集整理3.晶体结构优化BP神经网络3.1 BP神经网络参数设置3.2 晶体结构算法应用 4.测试结果…

实现vue导出excel和echart图形分别在不同工作表

背景 实现一键导出excel 并且区分图表和表格为不同的sheet工作表 最终效果为 代码实现 功能实现 <script lang"ts">import * as echarts from echarts;import ExcelJS from exceljs;import { saveAs } from file-saver; import {getAsyncTempCurrentData} …

基于社交网络优化的BP神经网络(分类应用) - 附代码

基于社交网络优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码 文章目录 基于社交网络优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码1.鸢尾花iris数据介绍2.数据集整理3.社交网络优化BP神经网络3.1 BP神经网络参数设置3.2 社交网络算法应用 4.测试结果…

linux进阶(脚本编程/软件安装/进程进阶/系统相关)

一般市第二种,以bash进程执行 shelle脚本编程 env环境变量 set查看所有变量 read设置变量值 echo用于控制台输出 类似java中的sout declear/typeset声明类型 范例 test用于测试表达式 if/else case while for 函数 脚本示例 软件安装及进阶 fork函数(复制一个进程(开启一个进…

TS 泛型你还不会?来!我教你

前言&#xff1a;最近遇到了一些写作上的烦恼&#xff0c;自己好像陷入了程序员的通病想法&#xff0c;“这个知识点这么简单&#xff0c;大家应该都会吧&#xff0c;我说出来是不是显得我很笨。” 思考了近一个月&#xff0c;又翻了翻自己最开始写作的文章&#xff0c;文笔虽…

python二次开发Solidworks:方程式驱动曲线

如果按照维度去划分&#xff0c;SOLIDWORKS中曲线可以划分为平面曲线和空间曲线&#xff0c;并且在二维草图还是3D草图中都提供了“方程式驱动曲线”。但是从使用方法来讲&#xff0c;方程式驱动的曲线分为两种定义方式:“显性”和“参数式”。“显式方程”在定义了起点和终点处…

核酸管外观缺陷检测(一)

1.1 应用示例思路 (1) 对核酸管图像进行灰度化、阈值分割和连通域分析&#xff1b; (2) 筛选出待检测的区域&#xff0c;并对该区域进行变换校正&#xff1b; (3) 进一步获取待检测的ROI区域&#xff0c;并根据几何特征和阈值条件&#xff0c;来对核酸管外观进行检测&#x…

openGauss学习笔记-103 openGauss 数据库管理-管理数据库安全-客户端接入之SSL证书管理-证书生成

文章目录 openGauss学习笔记-103 openGauss 数据库管理-管理数据库安全-客户端接入之SSL证书管理-证书生成103.1 操作场景103.2 前提条件103.3 自认证证书生成过程 openGauss学习笔记-103 openGauss 数据库管理-管理数据库安全-客户端接入之SSL证书管理-证书生成 openGauss默认…

【密评】商用密码应用安全性评估从业人员考核题库(十)

商用密码应用安全性评估从业人员考核题库&#xff08;十&#xff09; 国密局给的参考题库5000道只是基础题&#xff0c;后续更新完5000还会继续更其他高质量题库&#xff0c;持续学习&#xff0c;共同进步。 2251 单项选择题 根据 GM/T 0030《服务器密码机技术规范》&#xff0…

系统架构师备考倒计时17天(每日知识点)

一、数据库设计阶段以及相应的产物 需求分析阶段&#xff1a;数据流图、数据字典、需求说明书&#xff1b;概念结构设计阶段&#xff1a;ER模型&#xff1b;逻辑结构设计阶段&#xff1a;关系模式&#xff1b;物理设计阶段&#xff1a;包括存储结构和存取方法的物理结构。 &…

35岁左右的项目经理,这5种能力一定要有​

大家好&#xff0c;我是老原。 经常有项目经理和我吐槽&#xff0c;现在不管是做项目&#xff0c;还是做管理&#xff0c;都太难了。 上有甲方和和老板给压力&#xff0c;下有团队成员叫苦连天&#xff0c;最后里外不是人。 刚毕业20多岁的时候还好&#xff0c;随着年龄的增…

(矩阵) 289. 生命游戏 ——【Leetcode每日一题】

❓ 289. 生命游戏 难度&#xff1a;中等 根据 百度百科 &#xff0c; 生命游戏 &#xff0c;简称为 生命 &#xff0c;是英国数学家约翰何顿康威在 1970 年发明的细胞自动机。 给定一个包含 m n 个格子的面板&#xff0c;每一个格子都可以看成是一个细胞。每个细胞都具有一…

【算法设计与分析qwl】伪码——顺序检索,插入排序

伪代码&#xff1a; 例子&#xff1a; 改进的顺序检索 Search(L,x)输入&#xff1a;数组L[1...n]&#xff0c;元素从小到大排序&#xff0c;数x输出&#xff1a;若x在L中&#xff0c;输出x位置下标 j ,否则输出0 j <- 1 while j<n and x>L[j] do j <- j1 if x<…

2022年全网最全最细最流行的自动化测试工具有哪些?

一&#xff1a;前言 随着测试工程师技能和工资待遇的提升&#xff0c;甚至有一部分的开发人员开始转入测试岗位&#xff0c;跨入自动化领域的测试攻城狮越来越多。在自动化测试领域&#xff0c;自动化工具肯定占据了核心的位置。 本文总结了常用的测试自动化工具和框架&#x…

QT_day2

使用手动连接&#xff0c;将登录框中的取消按钮使用qt4版本的连接到自定义的槽函数中&#xff0c;在自定义的槽函数中调用关闭函数 将登录按钮使用qt5版本的连接到自定义的槽函数中&#xff0c;在槽函数中判断ui界面上输入的账号是否为"admin"&#xff0c;密码是否为…

rust学习——函数返回值

概念 Rust 中的函数定义以 fn 开始&#xff0c;后跟着函数名和一对圆括号。大括号告诉编译器函数体在哪里开始和结束。 特殊的地方——函数返回值 错误的写法 正解1 去掉分号 fn main() {let x plus_one(5);println!("The value of x is: {}", x); }fn plus_…

c++_learning-进阶部分

文章目录 类&#xff1a;抽象类&#xff1a;类、类作用域&#xff1a;类成员访问权限&#xff1a;使用细节&#xff1a;成员函数的修饰符const、mutable&#xff1a;const在类中的使用&#xff1a;mutable&#xff1a; this&#xff1a;返回自身对象的引用构造函数&#xff08;…