手写redux和applyMiddleware中间件react示例

目录

[一 核心代码](#一 核心代码)

1.reducer

2.store.js

[二 关于context API的使用](#二 关于context API的使用)

[1. MyContext](#1. MyContext)

[2. createContext](#2. createContext)

[3. ContextProvider](#3. ContextProvider)

[4. connect](#4. connect)

[三 组件验证效果](#三 组件验证效果)

[1. Todo](#1. Todo)

[2. TodoList](#2. TodoList)

3.TodoItem

4.TodoInput

[5. App组件引入Todo组件](#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 => arg
  return 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.dispatch
      const 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"]}>
      <input
        type="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组件

相关推荐
nvd1110 分钟前
企业级 LLM 实战:在受限环境中基于 Copilot API 构建 ReAct MCP Agent
前端·copilot
Dragon Wu19 分钟前
TailWindCss cva+cn管理样式
前端·css
烤麻辣烫24 分钟前
Web开发概述
前端·javascript·css·vue.js·html
Front思35 分钟前
Vue3仿美团实现骑手路线规划
开发语言·前端·javascript
徐同保37 分钟前
Nano Banana AI 绘画创作前端代码(使用claude code编写)
前端
Ulyanov38 分钟前
PyVista与Tkinter桌面级3D可视化应用实战
开发语言·前端·python·3d·信息可视化·tkinter·gui开发
计算机程序设计小李同学38 分钟前
基于Web和Android的漫画阅读平台
java·前端·vue.js·spring boot·后端·uniapp
lkbhua莱克瓦2440 分钟前
HTML与CSS核心概念详解
前端·笔记·html·javaweb
沛沛老爹41 分钟前
从Web到AI:Agent Skills CI/CD流水线集成实战指南
java·前端·人工智能·ci/cd·架构·llama·rag
GISer_Jing1 小时前
1.17-1.23日博客之星投票,每日可投
前端·人工智能·arcgis