preface

The word “higher-order” sounds very intimidating, because the higher-order equations in advanced math classes in college are so maddening that people who are first introduced to the concept of higher-order components think it’s some kind of advanced thinking and complex logic. But I believe that after you finish learning and use a lot of production environment, you will find that the so-called “advanced component” is not advanced at all, very simple to understand. This article takes you through the React advanced components by answering three questions.

1. Why are higher-order components needed?

2. What are the higher-order components?

3. How to implement higher-order components?


1. Why are higher-order components needed?

The question is simple, why do we need React /vue/angular? One of the core reasons to use frameworks is to improve development efficiency and leave work early. Similarly, the React higher-order component allows us to write more maintainable React code and leave work earlier

For example, ES6 supports the use of import/export to split code functions and modules to avoid “lumps” of code in a single file. Similarly, if a complex React component has dozens of custom functions, it needs to be split. Otherwise, it will become a “pile” of components. How do you gracefully split components? React Advanced components were born.

When writing React code in ES5, you can split it using the traditional pattern of mixins. React fully supports ES6 and promotes writing JSX in ES6, while eliminating mixins. As a result, higher-order components are increasingly valued by the open source community, and well-known third-party libraries such as Redux make extensive use of higher-order components.

2. What are the higher-order components?

Before answering this question, let’s take a look at the cover picture of this article. Why does the author use the one-variable function that junior high school students have mastered to represent this article? Obviously, a higher-order function is something of the form y=kx+b, where x is the original component we want to modify, and y is the output component after modification. So how exactly did that work? K and B are the methods of modification. This is the basic principle of high order components, is not high order ~

Another example is believed to make you understand: we need to add the code, so we separate out the method of the addition calculation into an addition function, this addition function can be called everywhere, thus reducing the amount of work and code. And the ubiquitous addition function that we’ve isolated from React is, by analogy, a higher-order component.

3. How to implement higher-order components?

The react component is a function that handles the React component. So how do we implement a higher-order component? There are two ways:

Property proxy 2. Reverse inheritance

The first method, the most common implementation, is to pass the props of the component being processed along with the new props to the new component as follows:

//WrappedComponent is the processed componentfunction HOC(WrappedComponent){
    return class HOC extends Component {
        render(){
            const newProps = {type:'HOC'};
            return<div> <WrappedComponent {... this.props} {... newProps}/> </div> } } } @HOC class OriginComponent extends Component {render() {return<div> is the original component </div>}} //const newComponent = HOC(OriginComponent)Copy the code

Property brokering sounds like a lot of work, but from the code, it’s just using HOC to add properties to the WrappedComponent being processed and return a new component containing the original component. From the Chrome debugger we can see that the original component has been wrapped and has the Type attribute:

The code above uses ES7’s decorator decorator to decorate and enhance OriginComponent components, or function methods in comments to achieve the same effect.

The advantage of using property brokers is that common methods can be isolated and reused multiple times. For example, if we implement an addition function, we can transform the addition function into the form of the above HOC function, and then wrap other components to use this method in the component.

The second method of reverse inheritance is interesting, first look at the code:

function HOC(WrappedComponent){
    returnClass HOC extends WrappedComponent {// Inherits the component passed intest1() {return this.test2() + 5;
        }
        
        componentDidMount(){
            console.log('1');
            this.setState({number:2});
        }

        render(){// Use super to call the render method of the passed componentreturn super.render();
        }
    }
}

@HOC
class OriginComponent extends Component {
    constructor(props){
        super(props);
        this.state = {number:1}
    }
    
    test2() {return 4;
    }
    componentDidMount(){
        console.log('2');
    }

    render() {return (
            <div>
                {this.state.number}{'and'} {this.test1()} this is the original component </div>)}} //const newComponent = HOC(OriginComponent)Copy the code

We may be a little confused after this code, so let’s first analyze the keyword “inheritance”. What is inheritance? The newly generated HOC component obtains all of the properties and methods of the incoming OriginComponent via the extends keyword, which is called “inheritance.” In other words, after inheritance, HOC can implement all functions of OriginComponent. Moreover, HOC can be modified with state and props to change the behavior of the component, which is called “render hijacking”. It can be said that the high-order components realized by reverse inheritance are more powerful, personalized and adaptable to more scenarios than the high-order components realized by property proxy.

In the code above, we can see:

First: this.test1() prints 9. Why is that? Because in ES6, super binds the subclass’s this when it calls a superclass method as an object. So when super.render() is executed, this in the OriginComponent points to HOC, and test1 is successfully executed.

Second: the console outputs 1 instead of 2. Why is that? First, the decorator executes at code compile time, so HOC’s render method executes before the Render method of The OriginComponent. HOC.__proto__ === OriginComponent. When executing componentDidMount, the method already exists in the child component, so it ends after execution. No longer continue looking up from __proto__. If we remove the componentDidMount method in child component HOC, the console outputs 2.

What happens when we have multiple higher-order components that need to be enhanced at the same time? We can write this:

@fun1
@fun2
@fun3
class OriginComponent extends Component {
    ...
}
Copy the code

You can also use lodash’s flowRight method:

const enchance = lodash.flowRight(fun1,fun2,fun3);

@enchance
class OriginComponent extends Component {
    ...
}
Copy the code

Since fun1, fun2, and fun3 are all functions that process classes, it’s just as long as the implementation processes the classes in sequence.

That’s all we have to say about higher-order component implementations. To fill in the gaps, there are two pertinent pieces of advice from the official document, which are reproduced here:

In a word, to avoid a screen full of HOC due to the presence of higher-order components during debugging (for example, the code above), you can set the displayName property of the class to change the name of the component.

If you’re familiar with inheritance in ES6, the above text should be pretty simple to understand. In ES6 inheritance, subclasses cannot inherit static methods of their parent class, that is, methods defined using the static keyword. If subclasses want to use it, they must copy it before they can use it.

To sum up, higher-order components are functions that deal with components, and they can be implemented in two ways: one is a property proxy, which is similar to y = x + b in the unary equation, where input X is the original component, parameter B is the property/method you want to add or remove, and y is the final output component. The second is reverse inheritance, which is similar to y = kx + b of the unary equation. State and props can be used for rendering hijacking (k) to change the behavior of the component.