• 手写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

    1. // 新增列表数据和改变数组数据
    2. // 将业务逻辑拆分到一个单独文件中,方便进行状态管理
    3. import _ from 'lodash';
    4. export interface StateProps {
    5. id: number;
    6. text: string;
    7. isFinished: boolean;
    8. }
    9. export interface ActionProps {
    10. type: string;
    11. [key: string]: any;
    12. }
    13. interface IStateObjectProps {
    14. pickerArr: StateProps[];
    15. filterTag: 'SHOW_ALL'|'SHOW_FINISHED'|'SHOW_NOT_FINISH';
    16. dispatch: any;
    17. }
    18. const reducer = (state: IStateObjectProps, action: ActionProps) => {
    19. console.log(state, action, 'reducer11111');
    20. const pickerArr0 = _.get(state, 'pickerArr')||[];
    21. switch (_.get(action, 'type')) {
    22. case "ADD":
    23. return {
    24. ...state,
    25. pickerArr: [...pickerArr0, _.get(action, 'todo')]
    26. };
    27. case "CHANGESTATUS":
    28. const pickerArr = _.map(pickerArr0, (item) => {
    29. if (item.id === action.id) {
    30. return Object.assign({}, item, { isFinished: !_.get(item, 'isFinished') });
    31. }
    32. return item;
    33. })||[];
    34. return {
    35. ...state,
    36. pickerArr,
    37. }
    38. case 'SET_VISIBILITY_FILTER':
    39. const filterTag = action.filterTag;
    40. return {
    41. ...state,
    42. filterTag,
    43. };
    44. default:
    45. return state || {};
    46. }
    47. };
    48. export default reducer

    2.store.js

    1. import React from 'react';
    2. import reducer from './reducer'
    3. // compose执行顺序就是从后往前
    4. const compose = (...funcs) => {
    5. if (funcs.length === 0) return arg => arg
    6. return funcs.reduce((v, cur) => (...args) => (v(cur(...args))))
    7. }
    8. function applyMiddleware(...args) {
    9. // 将中间件们变成一个数组
    10. const middlewares = Array.from({ length: args.length }, (_, _key) => args[_key]);
    11. return function (createStore) {
    12. return function () {
    13. const store = createStore.apply(void 0, arguments);
    14. const middlewareAPI = {
    15. getState: store.getState,
    16. dispatch: store.dispatch,
    17. };
    18. // map遍历中间件, 执行监听器函数, 形成新数组
    19. const chain = middlewares.map((middleware) => middleware(middlewareAPI));
    20. // 展开中间件,调整执行顺序,并传入store.dispatch
    21. const dispatch = compose(...chain)(store.dispatch);
    22. // 返回需要的存储数据(将dispatch合并进store对象)
    23. return {
    24. ...store,
    25. dispatch,
    26. };
    27. };
    28. };
    29. }
    30. function legacy_createStore(reducer, preloadedState) {
    31. let state = preloadedState || null;
    32. const listeners = [];
    33. const subscribe = (fn) => listeners.push(fn);
    34. const getState = () => state;
    35. const dispatch = (action) => {
    36. const state1 = reducer(state, action);
    37. state = state1
    38. // 因为是在获取到最新的state的值之后有执行的监听回调, 所以使用store.subscribe可以监听到最新的state的值!!!
    39. listeners.forEach((fn) => fn());
    40. return state
    41. }
    42. return { getState, dispatch, subscribe }
    43. }
    44. function createStore(reducer, preloadedState, enhancer) {
    45. if (typeof preloadedState === 'function') {
    46. return preloadedState(legacy_createStore)(reducer)
    47. }
    48. if (typeof enhancer === 'function') {
    49. return enhancer(legacy_createStore)(reducer, preloadedState)
    50. }
    51. return legacy_createStore(reducer, preloadedState)
    52. }
    53. function createThunkMiddleware(extraArgument) {
    54. return ({ dispatch, getState }) => next => action => {
    55. if (typeof action === 'function') {
    56. return action(dispatch, getState, extraArgument)
    57. }
    58. return next(action)
    59. }
    60. }
    61. const thunk = createThunkMiddleware('xxxxxx');
    62. const store = applyMiddleware(thunk)(createStore)(reducer);
    63. // 或者
    64. // const store = createStore(reducer, applyMiddleware(thunk));
    65. // 或者
    66. // const store = createStore(reducer);
    67. console.log(store, 'oldState======')
    68. store.subscribe(() => {
    69. const newState = store.getState()
    70. // 数据可能变化,需要监听最新的
    71. console.log(newState, 'newState====');
    72. })
    73. export default store;

    二 关于context API的使用

    1. MyContext

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

    2.  createContext

    1. import React, {ReactNode, memo} from "react";
    2. // 已经使用了React.createContext, 这个可以忽略, 只是为了展示createContext功能的简单代码
    3. const createContext = ({}) => {
    4. let value = {};
    5. const Provider = memo((props: {
    6. children: ReactNode;
    7. value:{
    8. dispatch: (arg1:any)=>void;
    9. getState:() => any;
    10. };
    11. }) => {
    12. value = props.value;
    13. return <>{props.children}</>;
    14. });
    15. const Consumer = memo(({ children }: { children: any }) => {
    16. return <>{typeof children === "function" ? children(value) : children}</>;
    17. });
    18. return { Provider, Consumer };
    19. };
    20. export default createContext;

    3. ContextProvider

    1. import React, { useState } from "react";
    2. import MyContext from "./MyContext";
    3. import _ from "lodash";
    4. import store from "./store";
    5. const useReducer = (state0, dispatch0) => {
    6. const [state, dispatch] = useState(state0);
    7. const dispatch1 = (action) => {
    8. dispatch0(action);
    9. dispatch(store.getState());
    10. };
    11. return [state, dispatch1]
    12. }
    13. // 父组件
    14. const ContextProvider = ({ children }) => {
    15. const [state, dispatch] = useReducer(store.getState(), store.dispatch);
    16. return <MyContext.Provider value={{
    17. getState: () => state,
    18. dispatch
    19. }}>{children}</MyContext.Provider>;
    20. };
    21. export default ContextProvider;

    4. connect

    1. import React from "react";
    2. import MyContext from "./MyContext";
    3. import _ from "lodash";
    4. // 模拟react-redux的 connect高阶函数
    5. const connect = (mapStateToProps, mapDispatchToProps) => {
    6. return (Component) => (props) =>
    7. wrapper(Component, { mapStateToProps, mapDispatchToProps, ...props });
    8. };
    9. const wrapper = (Comp, props) => {
    10. const { mapStateToProps, mapDispatchToProps, ...rest } = props;
    11. return (
    12. <MyContext.Consumer>
    13. {(store) => {
    14. const dispatchs = mapDispatchToProps(_.get(store, 'dispatch'));
    15. let states1 = mapStateToProps(_.get(store, 'getState') ? _.get(store, 'getState')(): {});
    16. return <Comp {...{ ...states1, ...dispatchs, ...rest }} />;
    17. }}
    18. </MyContext.Consumer>
    19. );
    20. };
    21. export default connect;

    组件验证效果

    1. Todo

    1. import React from "react";
    2. import TodoInput from "./TodoInput";
    3. import TodoList from "./TodoList";
    4. import ContextProvider from "./ContextProvider";
    5. // 父组件
    6. const Todo = () => {
    7. return (
    8. <ContextProvider>
    9. <TodoInput />
    10. <TodoList />
    11. </ContextProvider>
    12. );
    13. };
    14. export default Todo;

    2. TodoList

    1. import React, { useEffect } from "react";
    2. import TodoItem from "./TodoItem";
    3. import _ from "lodash";
    4. import connect from "./connect";
    5. import { mapStateTotProps } from "./mapStateToProps";
    6. import { mapDispatchToProps } from "./mapDispatchToProps";
    7. import styles from './TodoList.scss'
    8. const TodoList = (props) => {
    9. const { todoList } = props;
    10. console.log(styles, 'TodoList-styles', props)
    11. return (
    12. <>
    13. <p className={styles.title}>checckbox-list: </p>
    14. <div className="todo-list">
    15. {_.map(todoList, (item) => (
    16. <TodoItem key={_.get(item, "id")} todo={item || {}} />
    17. ))}
    18. </div>
    19. <hr />
    20. </>
    21. );
    22. };
    23. export default connect(mapStateTotProps, mapDispatchToProps)(TodoList);

    3.TodoItem

    1. import _ from 'lodash';
    2. import React from "react";
    3. import connect from './connect';
    4. import { mapStateTotProps } from "./mapStateToProps";
    5. import { mapDispatchToProps } from "./mapDispatchToProps";
    6. // 孙子组件
    7. const TodoItem = (props: any) => {
    8. const { todo, changeTodo } = props;
    9. // 改变事项状态
    10. const handleChange = () => {
    11. changeTodo(_.get(todo, 'id'));
    12. }
    13. return (
    14. <div className="todo-item">
    15. <input type="checkbox" checked={todo.isFinished} onChange={handleChange} />
    16. <span style={{ textDecoration: _.get(todo, 'isFinished') ? 'line-through' : 'none' }}>{todo.text}</span>
    17. </div>
    18. )
    19. }
    20. export default connect(mapStateTotProps, mapDispatchToProps)(TodoItem);

    4.TodoInput

    1. import React, { useState } from "react";
    2. import connect from './connect';
    3. import { mapStateTotProps } from "./mapStateToProps";
    4. import { mapDispatchToProps } from "./mapDispatchToProps";
    5. import styles from './TodoInput.scss'
    6. // 子组件
    7. const TodoInput = (props) => {
    8. // console.log(styles, 'styles', props)
    9. const [text, setText] = useState("");
    10. const {
    11. addTodo,
    12. showAll,
    13. showFinished,
    14. showNotFinish,
    15. } = props;
    16. const handleChangeText = (e: React.ChangeEvent) => {
    17. setText((e.target as HTMLInputElement).value);
    18. };
    19. const handleAddTodo = () => {
    20. if (!text) return;
    21. addTodo({
    22. id: new Date().getTime(),
    23. text: text,
    24. isFinished: false,
    25. });
    26. setText("");
    27. };
    28. return (
    29. <div className={styles["todo-input"]}>
    30. <input
    31. type="text"
    32. placeholder="请输入代办事项"
    33. onChange={handleChangeText}
    34. value={text}
    35. className="aaa"
    36. />
    37. <button className={styles.btn} onClick={handleAddTodo}>+添加</button>
    38. <button className={styles.btn} onClick={showAll}>show all</button>
    39. <button className={styles.btn} onClick={showFinished}>show finished</button>
    40. <button className={styles.btn} onClick={showNotFinish}>show not finish</button>
    41. </div>
    42. );
    43. };
    44. export default connect(mapStateTotProps, mapDispatchToProps)(TodoInput);

    5. App组件引入Todo组件

  • 相关阅读:
    【Touchstone 1.0&2.0数据格式解析】
    计算机图形学之点和直线
    基于虚拟同步发电机控制的双机并联Simulink仿真模型
    C++面向对象三大特性之一------继承
    【SwiftUI模块】0008、SwiftUI-自定义启动闪屏动画-App启动闪屏曲线路径动画
    从闪亮开始,到耀眼不止——浅谈飞利浦骨传导耳机的进化史
    云计算-Linux-软链接与硬链接,获取命令帮助,系统运行级别,关机和重启
    EL-input添加双击或者单击事件
    大一作业HTML网页作业 HTML CSS制作二十四节气网页
    Python | Leetcode Python题解之第203题移除链表元素
  • 原文地址:https://blog.csdn.net/qq_42750608/article/details/136272987