【前端知识】React 基础巩固(三十七)——自定义connect高阶组件

React 基础巩固(三十七)------自定义connect高阶组件

一、手撸一个自定义connect高阶组件

javascript 复制代码
import { PureComponent } from "react";
import store from "../store";

/**
 * connect的参数:
 * 参数一: 函数
 * 参数二: 函数
 * 返回值: 函数
 */
export default function connect(mapStateToProps, mapDispatchToProps) {
  // 返回一个高阶组件,本质也是函数
  return function (WrapperComponent) {
    class NewComponent extends PureComponent {
      constructor(props) {
        super(props);
        // 将接收到的mapStateToProps赋给state,用于部分值修改时的浅层比较、更新state
        this.state = mapStateToProps(store.getState());
      }
      componentDidMount() {
        this.unsubscribe = store.subscribe(() => {
          this.setState(mapStateToProps(store.getState()));
        });
      }

      componentWillUnmount() {
        this.unsubscribe();
      }

      render() {
        // 将接收到的mapStateToProps、mapDispatchToProps传入要返回的新组件中
        const stateObj = mapStateToProps(store.getState());
        const dispatchObj = mapDispatchToProps(store.dispatch);
        return (
          <WrapperComponent {...this.props} {...stateObj} {...dispatchObj} />
        );
      }
    }
    return NewComponent;
  };
}

二、目前的问题

javascript 复制代码
import store from "../store";

从这行代码可以看到,目前的connect直接引用了上级目录的store,过于依赖目前既定的store,这样不利于复用。假设另一个项目的store所在位置不在上级目录中,则会出现问题。

三、优化上面的丐版connect

为了让所有人都能使用,我们应该把这种"写死"的做法换成让开发者自己传入一个store:

  1. 构建一个StoreContext,用于创建Store的上下文(src/hoc/StoreContext.js):

    javascript 复制代码
    import { createContext } from "react";
    
    export const StoreContext = createContext()
  2. 当我们在项目的index.js中引入connect时,引入并使用该上下文,让开发者手动传入当前的store(src/index.js):

    javascript 复制代码
    import React from "react";
    import ReactDOM from "react-dom/client";
    import { Provider } from "react-redux";
    import { StoreContext } from "./hoc";
    import App from "./App";
    import store from "./store";
    
    const root = ReactDOM.createRoot(document.getElementById("root"));
    root.render(
      // <React.StrictMode>
    
      <Provider store={store}>
        <StoreContext.Provider value={store}>
          <App />
        </StoreContext.Provider>
      </Provider>
      // </React.StrictMode>
    );
  3. 在connect中,通过 contextType 共享从 Provider 中传入的 store 变量,将原来直接引用的 store 替换成 this.context(hoc/connect.js):

    javascript 复制代码
    import { PureComponent } from "react";
    import { StoreContext } from "./StoreContext";
    
    /**
     * connect的参数:
     * 参数一: 函数
     * 参数二: 函数
     * 返回值: 函数
     */
    export function connect(mapStateToProps, mapDispatchToProps) {
      // 返回一个高阶组件,本质也是函数
      return function (WrapperComponent) {
        class NewComponent extends PureComponent {
          constructor(props, context) {
            super(props);
            // 将接收到的mapStateToProps赋给state,用于部分值修改时的浅层比较、更新state
            this.state = mapStateToProps(context.getState());
          }
          componentDidMount() {
            this.unsubscribe = this.context.subscribe(() => {
              this.setState(mapStateToProps(this.context.getState()));
            });
          }
    
          componentWillUnmount() {
            this.unsubscribe();
          }
    
          render() {
            // 将接收到的mapStateToProps、mapDispatchToProps传入要返回的新组件中
            const stateObj = mapStateToProps(this.context.getState());
            const dispatchObj = mapDispatchToProps(this.context.dispatch);
            return (
              <WrapperComponent {...this.props} {...stateObj} {...dispatchObj} />
            );
          }
        }
    
        // 在类组件中,通过 contextType 共享store变量
        NewComponent.contextType = StoreContext
    
        return NewComponent;
      };
    }
  4. 最后,在hoc中构建index.js,将优化后的connect导出(hoc/index.js):

    javascript 复制代码
    export { StoreContext } from "./StoreContext";
    export { connect } from "./connect";
  5. 在界面中使用现在优化后的connect:

    javascript 复制代码
    import React, { PureComponent } from "react";
    import { connect } from "../hoc";
    import { addNumber } from "../store/features/counter";
    
    export class About extends PureComponent {
      render() {
        const { counter } = this.props;
        return (
          <div>
            <h2>About Counter: {counter}</h2>
          </div>
        );
      }
    }
    
    const mapStateToProps = (state) => ({
      counter: state.counter.counter,
    });
    
    const mapDispatchToProps = (dispatch) => ({
      addNumber(num) {
        dispatch(addNumber(num));
      },
    });
    
    export default connect(mapStateToProps, mapDispatchToProps)(About);
  6. 查看效果,与之前效果一致:

相关推荐
转转技术团队6 分钟前
多代理混战?用 PAC(Proxy Auto-Config) 优雅切换代理场景
前端·后端·面试
南囝coding8 分钟前
这几个 Vibe Coding 经验,真的建议学!
前端·后端
gnip21 分钟前
SSE技术介绍
前端·javascript
yinke小琪36 分钟前
JavaScript DOM节点操作(增删改)常用方法
前端·javascript
枣把儿40 分钟前
Vercel 收购 NuxtLabs!Nuxt UI Pro 即将免费!
前端·vue.js·nuxt.js
望获linux41 分钟前
【Linux基础知识系列】第四十三篇 - 基础正则表达式与 grep/sed
linux·运维·服务器·开发语言·前端·操作系统·嵌入式软件
爱编程的喵43 分钟前
从XMLHttpRequest到Fetch:前端异步请求的演进之路
前端·javascript
喜欢吃豆1 小时前
深入企业内部的MCP知识(三):FastMCP工具转换(Tool Transformation)全解析:从适配到增强的工具进化指南
java·前端·人工智能·大模型·github·mcp
豆苗学前端1 小时前
手把手实现支持百万级数据量、高可用和可扩展性的穿梭框组件
前端·javascript·面试
不见_1 小时前
不想再写周报了?来看看这个吧!
前端·命令行