React High-Order Component HOC Usage Summary

React High-Order Component HOC Usage Summary

One sentence to introduce HOC

What is a higher-order component (HOC)? According to the official documentation: "Higher-order components are an advanced technology for reusing component logic in react. It is not part of the react API, but a pattern that is extracted from the compositional nature of react itself. Specifically, a higher-order component is a function that accepts a component as a parameter and then returns a new component.

Usage scenarios

Extract the methods and react features (such as side effects in the life cycle) of several components with similar functions into HOC, and then pass the components to be encapsulated to HOC. Finally, pass the common methods to the components.

Advantages

Make the code concise and elegant, with less code

HOC (High-Order Component)

/*
  HOC (High-Order Component): Receives a component and returns a packaged component (enhanced component)
    - Not a React API
    - is a design pattern, similar to the decorator pattern - ≈ Mixin && > Minxin

  const wrapped component = higher-order component (wrapped component);
  // eg const Wrapper = withRouter(NavBar);


  The high-order component will pass all received props to the wrapped component (transparent transmission)
  Ref is similar to key. It is not a prop, so it will not be passed through. Ref will be bound to the outer packaging container. For the solution, please refer to the following <<Handling ref>>
* */

How to package components?

/*
  How to package components?

  The first one: When packaging export normally, package import React from 'react';
      import Hoc from './Hoc';

      class Header extends React.Component {
        render() {
          return <span>{ this.props.count }</span>
        }
      };

      export default Hoc(Header);

    ==========

    Package after import:
      import Header from './header';
      import Hoc from './Hoc';

      const EnhanceHeader = Hoc(Header);

      const Home = () => {
        return (
          <div>
             <EnhanceHeader count={1} />
          </div>
        )
      }

  The second type: Decorator packaging, can only be used in class components import React from 'react';
    import Hoc from './Hoc';

    @Hoc
    export default class Header extends React.Component {
      render() {
        return <span>{ this.props.count }</span>
      }
    };

    =======

    @Hoc
    class Header extends React.Component {
      render() {
        return <span>{ this.props.count }</span>
      }
    };

    export default Header;
* */

Defining a simple HOC

/*
  Define a simple HOC that receives a component and returns a component import React from 'react';

  // Return class component export default function Hoc(WrappedComponent) {
    /*
      return class extends React.Component {}
        - The name displayed in React Developer Tools is Component

      return class Wrapper extends React.Component {}
        - The name displayed in React Developer Tools is Wrapper
    *\
    return class extends React.Component {
      render() {
        return <WrappedComponent {...this.props} />;
      }
    };
  }

  // Return a functional component export default function Hoc(WrappedComponent) {
    /*
      return function(props) {}
        - The name displayed in React Developer Tools is Anonymous

      return function Wrapper(props) {}
        - The name displayed in React Developer Tools is Wrapper
    *\
    return function Wrapper(props) {
      return <WrappedComponent {...props} />;
    };
  }
* */

Passing parameters to Hoc

/*
  Pass parameters to Hoc // Hoc can accept any parameters export default function Hoc(WrappedComponent, title, user, data) {
      return class Wrapper extends React.Component {
        render() {
          return <WrappedComponent {...this.props} />
        }
      };
    };

    // Pass parameters when packaging const EnhanceHeader = Hoc(Header, 'title', { name: '霖'}, [1, 2, 3]);
* */

Hoc Nesting

/*
  Hoc nesting, the principle of function currying // Hoc1: add title attribute to the component export default function Hoc1(WrappedComponent, title) {
    return class extends React.Component {
      render() {
        return <WrappedComponent title={title} {...this.props} />
      }
    };
  };

  // Hoc2: Modify the display content of the component export default function Hoc2(WrappedComponent, content) {
    return class extends WrappedComponent { // Reverse inheritance is used here render() {
        const elementTree = super.render(); // React uses Js objects to simulate the Dom tree structure. You can manipulate data by modifying the properties of Js objects console.log(elementTree); // If you don't know much about the structure inside, you can print it out + official website cloneElement() to learn more const newElementTree = React.cloneElement(elementTree, { children: `Your content has been hijacked: ${content}` });

        return newElementTree;
      }
    };
  };

  // Wrapped component export default class Header extends React.Component {
    render() {
      const { title } = this.props;

      return (
          <span title={title}>
            Default content</span>
        )
    }
  };

  // Use import Hoc1 from './Hoc1';
  import Hoc2 from './Hoc2';

  /*
    Packaging process 1. const Wrapper = Hoc2(Header, 'content');
    2. Hoc1 (Wrapper)
  **
  const EnhanceHeader = Hoc1(Hoc2(Header, 'Content'), 'Title');

  export default function Home() {
    return (
      <div>
        <EnhanceHeader />
      </div>
    );
  };

* */

Handling refs

/*
  Handling refs
  eg Hoc1(Hoc2(Content))

  <Content ref={myRef} /> The ref bound to Content will be bound to Hoc1 and will not be passed down to the first method React.forwardRef ================

      Use React.forwardRef() to process ref outside Hoc1 and use props to pass ref
      0. Wrap forwardRef outside the high-order component, intercept and obtain ref, add a props (xxx={ref}), and the real component is obtained through props.xxx1. Pass ref={XXXX} when using // Difference from the second method2. Use the second parameter of forwardRef to obtain ref
      3. Add a new props to forward ref downwards, eg forwardedRef={ref}
      4. Bind ref={props.forwardedRef} in the real component

      const Home = (props) => {
        const connectRef = useRef(null);

        return (
          <div>
            <Content ref={connectRef} />
          </div>
        );
      };

      // Wrapped component const Content = (props) => {
        return (
          <div>
            <input type="password" ref={props.forwardedRef} />
          </div>
        );
      };


      // The second input parameter of forwardRef can receive ref, and process ref in the outer layer of Hoc export default React.forwardRef((props, ref) => {
        const Wrapper = React.memo(Content); // Hoc

        // forwardRef wraps Wrapper
        // Need to pass ref down to the real component in Wrapper // Add a props attribute in Wrapper and pass the ref object as props to the child component return <Wrapper {...props} forwardedRef={ref} />;
      });

  The Second Method ==========

  0. Use a props to save ref when using
  1. Pass xxx={ref} when using // Difference from the first method 2. Bind ref={props.xxx} in the real component

  const Home = (props) => {
    const connectRef = useRef(null);

    return (
      <div>
        <Content forwardedRef={connectRef} />
      </div>
    );
  };

  // Define a higher-order component export const Hoc = (WrappedComponent) => {
    class Wrapper extends React.Component {
      render() {
        return <WrappedComponent {...props} />
      }
    }
  }

  // Wrapped component const Content = (props) => {
    return (
      <div>
        <input type="password" ref={props.forwardedRef} />
      </div>
    );
  };

  // Packaging process export default Hoc(Content);

* */

Using static methods of wrapped components

/*
  Use the static method of the wrapped component // Wrapped component, add static properties and methods export default class Header extends React.Component {
    static displayName = 'header';
    static showName = () => {
      console.log(this.displayName);
    };

    render() {
      return <span>header</span>
    }
  };

  //HOC
  export default function Hoc(WrappedComponent) {
    return class Wrapper extends React.Component {
      render() {
        return <WrappedComponent {...this.props} />
      }
    };
  };

  ===========

  // The component after Hoc packaging cannot get the static method import Header from './header';
    import Hoc from './Hoc';

    const EnhanceHeader = Hoc(Header);

    export default function Home() {
      console.log(EnhanceHeader.displayName); // undefined
      EnhanceHeader.showName(); // undefined

      return <EnhanceHeader />
    }

  =============

  // Solution 1: Copy static methods to HOC export default function Hoc(WrappedComponent) {
      return class Wrapper extends React.Component {
        static displayName = WrappedComponent.displayName; // Must know what static methods are in the wrapped component static showName = WrappedComponent.showName;

        render() {
          return <WrappedComponent {...this.props} />
        }
      };
    };

  ==============

  // Solution 2: Automatically copy all static properties and methods import React from 'react';
    import hoistNonReactStatic from 'hoist-non-react-statics';

    export default function Hoc(WrappedComponent) {

      class Wrapper extends React.Component {
        render() {
          return <WrappedComponent {...this.props} />
        }
      };

      hoistNonReactStatic(Wrapper, WrappedComponent);
      return Wrapper;
    };

  ==============

    // Solution 3: When exporting components, import additional static properties and methods class Header extends React.Component {
        render() {
          return <span>header</span>
        }
      };

      const displayName = 'header';

      function showName() {
        console.log(Header.displayName);
      };

      Header.displayName =displayName;
      Header.showName = showName;

      export default Header
      export { displayName, showName }

    // Import import Header, { displayName, showName } from './header';
      import Hoc from './Hoc';

      const EnhanceHeader = Hoc(Header);

      export default function Home() {
        console.log(displayName); // header
        showName(); //header

        return <EnhanceHeader />
      }
* */

Intercept props passed to the wrapped component and add, delete, and modify props

/*
  Intercept props passed to the wrapped component, add, delete, and modify props export default function Hoc(WrappedComponent) {

    return class Wrapper extends React.Component {
      render() {
        // Filter some props that are only used in the current Hoc, without unnecessary transparent transmission const { forMeProps, forOtherProps } = this.props;

        // Define inside this HOC, additional properties or methods that need to be injected into the wrapped component const injectProps = some-state-or-method; // Usually state or instance method // Pass upper-level props + additional props to the wrapped component
        return (
          <WrappedComponent
            injectProps={injectProps} // pass additional props that need to be injected
            {...forOtherProps} // pass through props related to follow-up
          />
        )
      }
    }
  }

  eg
    Hoc receives an additional props 'dealUpper', if true, converts data to uppercase dealUpper is only used in this Hoc, so there is no need to pass it to the wrapped component // HOC
  export default function Hoc(WrappedComponent) {
    return class Wrapper extends React.Component {
      render() {
        const { dealUpper, ...forOtherProps } = this.props;
        const { data } = forOtherProps;

        if (dealUpper) {
          Object.assign(forOtherProps, {data: data.toUpperCase()})
        }

        return <WrappedComponent { ...forOtherProps } />
      }
    };
  };

  // Export the enhanced component after Hoc packaging import React from 'react';
  import Hoc from './Hoc1';

  class Header extends React.Component {
    render() {
      console.log(this.props); // { data: 'ABC' }

      return <span>{this.props.data}</span>
    }
  };

  export default Hoc(Header); // Export the packaged enhanced component // Import using import Header from './header';

  const Home = () => {
    return <Header data={'abc'} dealUpper />
  }
* */

Use HOC to extract some complex common logic and extend different functions in different components

/*
  Use HOC to extract some complex common logic and extend different functions in different components. import React from 'react';

  export const Hoc = (WrappedComponent, namespace) => {
    class Wrapper extends React.Component {
      state = {
        data: []
      }

      // The same request method extracted componentDidMount = () => {
        const { dispatch } = this.props;

        dispatch({
          type: `${namespace}/queryData`, // Dynamically request different stores
          payload: {},
          callback: res => {
            if (res) {
              this.setState({
                data: res.data
              })
            }
          }
        })
      }

      render() {
        return <WrappedComponent { ...this.props } data={this.state.data} />
      }
    }
  }

  //Package A componentimport Hoc from './Hoc';

  const A = ({ data }) => {
    ... Omit the logic of requesting data return (data.map(item => item));
  }

  export default MyHoc(A, 'a');

  //Package B componentimport Hoc from './Hoc';

  const B = ({ data }) => {
    ... Omit the logic of requesting data return (
      <ul>
        {
          data.map((item, index) => {
            return <li key={index}><{item}/li>
          }
        }
      </ul>
    )
  }

   export default Hoc(B, 'b');
* */

Turning uncontrolled components into controlled components

/*
  Make uncontrolled components into controlled components // Hoc component export default function Hoc(WrappedComponent) {
    return class Wrapper extends React.Component {
      state = {
        value: ''
      };

      onChange = (e) => {
        this.setState({
          value: e.target.value
        })
      };

      render() {
        const newProps = {
          value: this.state.value,
          onChange: this.onChange
        };

        return <WrappedComponent {...this.props} {...newProps} />
      }
    };
  };

  // Ordinary component class InputComponent extends React.Component {
    render() {
      return <input {...this.props} />
    }
  }

  // Wrap export default Hoc(InputComponent);
* */

Reverse inheritance

/*
  Reverse inheritance (using the state and methods inside the wrapped component in Hoc)
    - The reverse inherited component must be a class component, not a function component export const Hoc = (WrappedComponent) => {
    class Wrapper extends WrappedComponent { // super ≈ this in WrappedComponent
      render() {
        if (!this.props.data) {
            return <span>loading....</span>
        } else {
            return super.render() //Call the render() method of the wrapped component}
      }
    }
  }

  ====

  export default function Hoc(WrappedComponent) {
    return class extends WrappedComponent {
      render() {
        const elementTree = super.render(); // React uses Js objects to simulate the Dom tree structure. You can manipulate data by modifying the properties of Js objects console.log(elementTree); // If you don't know much about the structure inside, you can print it out + official website cloneElement() to learn more const newElementTree = React.cloneElement(elementTree, { children: `Your content has been hijacked` });

        return newElementTree;
      }
    };
  };
* */

Render Hijacking

/*
  Rendering hijacking, eg, controlling whether a component renders (you can do a global loading effect, display loading when there is no data...)

    // Basic implementation export const LoadingHoc = (WrappedComponent) => {
      class Wrapper extends React.Component {
        render() {
          if (!this.props.data) {
            return <span>loading....</span>
          } else {
            return <WrappedComponent {...this.props} />
          }
        }
      }
    }

    // Use reverse inheritance to implement export const LoadingHoc = (WrappedComponent) => {
      class Wrapper extends WrappedComponent { // super ≈ this in WrappedComponent
        render() {
          if (!this.props.data) {
            return <span>loading....</span>
       	  } else {
            return super.render() //Call the render() method of the wrapped component}
        }
      }
    }

  ======

  eg hijack rendered content export default function Hoc2(WrappedComponent) {
      return class extends WrappedComponent { // Reverse inheritance is used here render() {
          const elementTree = super.render(); // React uses Js objects to simulate the Dom tree structure. You can manipulate data by modifying the properties of Js objects console.log(elementTree); // If you don't know much about the structure inside, you can print it out + official website cloneElement() to learn more const newElementTree = React.cloneElement(elementTree, { children: `Your content has been hijacked` });

          return newElementTree;
        }
      };
    };
* */

Configure the package name

/*
  Configure the wrapper name: It is easier to find in the debugging tool React Developer Tools. For example, the higher-order component is Hoc, and the wrapped component is WrappedComponent. The displayed name should be Hoc(WrappedComponent)


    // Return class component export default function Hoc(WrappedComponent) {
      return class extends React.Component {
        /*
          static displayName = 'XXX'; is not defined in Hoc
            - The name displayed in React Developer Tools is Anonymous

          static displayName = 'XXX'; is not defined in the packaged component
            - The name displayed in React Developer Tools is undefined Hoc

          Define static displayName = 'header' in the packaged component;
            - The name displayed in React Developer Tools is header Hoc
        *\
        static displayName = `Hoc(${WrappedComponent.displayName});

        render() {
          return <WrappedComponent {...this.props} />;
        }
      };
    }

    // Return a functional component export default function Hoc(WrappedComponent) {

      /*
        return function(props) {}
          - The name displayed in React Developer Tools is Anonymous

        return function Wrapper(props) {}
          - The name displayed in React Developer Tools is Wrapper
      *
      return function Wrapper(props) {
        return <WrappedComponent {...props} />;
      };
    }

    =======

    export default function Hoc(WrappedComponent) {
      const Wrapper = (props) => {
        return <WrappedComponent {...props} />;
      };

      /*
        static displayName = 'XXX'; is not defined in the packaged component
          - The name displayed in React Developer Tools is undefined Hoc

        Define static displayName = 'header' in the packaged component;
          - The name displayed in React Developer Tools is header Hoc
      *\
      Wrapper.displayName = `Hoc(${WrappedComponent.displayName})`;

      return Wrapper;
    }

    =====


    // Wrapped component export default class Header extends React.Component {
      static displayName = 'header';

      render() {
        return <span>{ this.props.count }</span>
      }
    };

* */

Don't use HOC in render

/*
  Don't use HOC in render

  eg
  export default class Home extends React.Component {
    render() {
      // Each render will create a new Wrapper
      // Wrapper1 !== Wrapper2
      // Causes the high-order component to be uninstalled and re-mounted, and the state will be lost (eg checkbox selection is lost | state is cleared)
      × const Wrapper = Hoc(WrappedComponent);

      return <Wrapper />
    }
  }

 =========

  √ const Wrapper = myHoc(WrappedComponent);

  export default class Home extends React.Component {
    render() {
      return <Wrapper />
    }
  }
* */

Hoc rendering order

/*
  Hoc rendering order Hoc (Header)

    componentDidMount: Header -> HOC
    componentWillUnMount: HOC -> Header
* */

HOC and Mixin

/*
  HOC and Mixin
    HOC
     - Belongs to the functional programming concept - The wrapped component cannot perceive the existence of the high-order component - The component returned by the high-order component will be enhanced on the original basis Mixin
    - Mixin mode will continuously add new properties and methods to the wrapped component - The wrapped component can perceive it - Need to be processed (naming conflict, state maintenance)
* */

The above is the detailed content of the usage of React high-order component HOC. For more information about React high-order component HOC, please pay attention to other related articles on 123WORDPRESS.COM!

You may also be interested in:
  • Teach you how to implement asynchronous loading of high-level components in react
  • React high-order components add and remove props
  • A brief talk about React high-order components
  • In-depth understanding of React high-order components
  • Detailed explanation of permission control for classic applications of react high-level components

<<:  CentOS8 installation tutorial of jdk8 / java8 (recommended)

>>:  CentOS 7.6 Telnet service construction process (Openssh upgrade battle first task backup transport line construction)

Recommend

Detailed explanation of the principles and usage of MySQL stored procedures

This article uses examples to explain the princip...

Vue makes div height draggable

This article shares the specific code of Vue to r...

HTML table tag tutorial (13): internal border style attributes RULES

RULES can be used to control the style of the int...

mysql5.7.17 installation and configuration example on win2008R2 64-bit system

123WORDPRESS.COM has explained to you the install...

Pure HTML+CSS to achieve Element loading effect

This is the effect of the Element UI loading comp...

Use CSS3 to implement button hover flash dynamic special effects code

We have introduced how to create a waterfall layo...

Let's talk about bitwise operations in React source code in detail

Table of contents Preface Several common bit oper...

How to install kibana tokenizer inside docker container

step: 1. Create a new docker-compose.yml file in ...

How to use explain to query SQL execution plan in MySql

The explain command is the primary way to see how...

What are the rules for context in JavaScript functions?

Table of contents 1. Rule 1: Object.Method() 1.1 ...

Solutions to MySql crash and service failure to start

I have been in contact with PHP for so long, but ...

How to draw a mind map in a mini program

Table of contents What is a mind map? How to draw...