[Front-end knowledge] React foundation consolidation (37) - custom connect high-level components

React foundation consolidation (37) - custom connect high-level components

1. Manually create a custom connect high-level component

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;
  };
}

2. Current problems

import store from "../store";

From this line of code, we can see that the current connect directly refers to the store in the upper-level directory, which is too dependent on the current established store, which is not conducive to reuse. Assuming that the store location of another project is not in the parent directory, problems will arise.

3. Optimize the beggars’ version connect above

In order to make it available to everyone, we should replace this "hard-coded" approach with allowing developers to pass in a store themselves:

  1. Build a StoreContext for creating the Store context (src/hoc/StoreContext.js):

    import {
          
           createContext } from "react";
    
    export const StoreContext = createContext()
    
  2. When we introduce connect in the index.js of the project, we introduce and use this context, allowing developers to manually pass in the current store (src/index.js):

    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. In connect, share the store variable passed in from Provider through contextType, and replace the original direct reference with store( this.contexthoc/connect.js):

    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. Finally, build index.js in hoc and export the optimized connect (hoc/index.js):

    export {
          
           StoreContext } from "./StoreContext";
    export {
          
           connect } from "./connect";
    
    
  5. Use the now optimized connect in the interface:

    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. Check the effect, which is consistent with the previous effect:

insert image description here

Guess you like

Origin blog.csdn.net/weixin_42919342/article/details/132000894