• Understanding higher-order Components in React
  • Chidume Nnamdi 🔥💻🎵🎮
  • The Nuggets translation Project
  • Permanent link to this article: github.com/xitu/gold-m…
  • Translator: ZavierTang
  • Proofread by xionglong58, TiaossuP

Understand the higher-order components in React

A complete guide to React advanced components

In my last article, we discussed type-checking in React; We saw how to specify the types of props in the React component, even though we were writing code in JS.

In this article, continuing with React, we’ll learn about HOC in React.

What is HOC?

HOC is an advanced use in React where a function (component) takes a component as an argument and returns a new component.

function composeComponent(Component) {
    return class extends React.Component {
        render() {
            return <Component />}}}Copy the code

Here, the function composeComponent takes a Component variable as an argument and returns an ES6 class defined Component. The returned class Component uses the Component variable in the argument. The Component argument will be a React Component that will be called by the returned class Component.

Such as:

class CatComponent extends React.Component {
    render() {
        return <div>Cat Component</div>}}Copy the code

We have a CatComponent that renders as follows:

Cat Component
Copy the code

We can pass CatComponet as an argument to composeComponent to get another component:

const composedCatComponent = composeComponent(CatComponent)
Copy the code

ComposedCatComponent components can also be rendered:

<composedCatComponent />
Copy the code

Render the result as follows:

Cat Component
Copy the code

This is similar to higher-order functions in JS.

Higher-order functions

A higher-order function is a pattern in JS in which a function takes one function as an argument and returns another function as a result. This is possible because of the syntactic nature of JS itself. This means the following types of data:

  • objects
  • arrays
  • strings
  • numbers
  • boolean
  • functions

Can be passed to a function as an argument or returned from a function.

function mul(x) {
    return (y) = > {
        return x * y
    }
}
const mulTwo = mul(2)

mulTwo(2) / / 4
mulTwo(3) / / 6
mulTwo(4) / / 8
mulTwo(5) / / 10
Copy the code

The muL function returns a function that captures the value of the variable x in a closure. Now, the returned function can use this x. Mul is now a higher-order function because it returns a function. This means we can call it to construct other, more specific functions by passing different arguments.

We can use it to create a function that returns 3 times the argument:

function mul(x) {
    return (y) = > {
        return x * y
    }
}
const triple = mul(3)

triple(2) / / 6
triple(3) / / 9
triple(4) / / 12
triple(5) / / 15
Copy the code

** What are the benefits of higher-order functions and components? ** When we find ourselves repeating the same logic over and over again. We need to find a way to encapsulate the same logic and call it from there. Higher-order functions provide a pattern that we can use to implement it.

From the example above, if we need to multiply by 3 multiple times in our program, we can create a function that returns another function that multiplicates the argument by 3, so whenever we need to multiply by 3, we can simply call the triple function obtained by passing the argument 3.

Using High-level Components (HOC)

So what are the benefits of using higher-order components in React?

Similarly, we might find ourselves repeating the same logic over and over again during the React project.

For example, we have an application for viewing and editing documents. We want to authenticate the users of the application so that only authenticated users can access the home page, edit the document, view the document, or delete the document. Our routing is designed like this:

<Route path="/" component={App}>
    <Route path="/dashboard" component={Documents}/>
    <Route path="document/:id/view" component={ViewDocument} />
    <Route path="documents/:id/delete" component={DelDocument} />
    <Route path="documents/:id/edit" component={EditDocument}/>
</Route>
Copy the code

We must validate in the Documents component so that only authenticated users can access it. As follows:

class Doucments extends React.Component {
    componentwillMount() {
        if(!this.props.isAuth){
            this.context.router.push("/")
        }
    }
    componentWillUpdate(nextProps) {
        if(! nextProps.isAuth) {this.context.router.push("/")            
        }
    }
    render() {
        return <div>Documents Paegs!!!</div>}}function mapstateToProps(state) {
    isAuth: state.auth
}
export default connect(mapStateToProps)(Documents)
Copy the code

State.auth saves the user’s authentication status. It has a value of false if the user is not authenticated, and true if the user is authenticated. The connect function maps state.auth to isAuth of the component props object. Then, when the component is about to mount to the DOM, componentWillMount is triggered, so we check that the isAuth of props is true. If true, the component continues rendering; Otherwise, the method switches the route to the “/” path, causing our browser to be redirected to the home page when rendering the Documents component, effectively blocking access to it by unauthenticated users.

When the component is rendered again after the initial rendering, we only perform the same action in componentWillUpdate to check if the user still has authorization, and if not, redirect to the home page as well.

We can then do the same in the ViewDocument component:

class ViewDoucment extends React.Component {
    componentwillMount() {
        if(!this.props.isAuth){
            this.context.router.push("/")
        }
    }
    componentWillUpdate(nextProps) {
        if(! nextProps.isAuth) {this.context.router.push("/")            
        }
    }
    render() {
        return <div>View Document Page!!!</div>}}function mapstateToProps(state) {
    isAuth: state.auth
}
export default connect(mapStateToProps)(ViewDocument)
Copy the code

In the EditDocument component:

class EditDocument extends React.Component {
    componentwillMount() {
        if(!this.props.isAuth){
            this.context.router.push("/")
        }
    }
    componentWillUpdate(nextProps) {
        if(! nextProps.isAuth) {this.context.router.push("/")            
        }
    }
    render() {
        return <div>Edit Document Page!!!</div>}}function mapstateToProps(state) {
    isAuth: state.auth
}
export default connect(mapStateToProps)(EditDocument)
Copy the code

In the DelDocument component:

class DelDocument extends React.Component {
    componentwillMount() {
        if(!this.props.isAuth){
            this.context.router.push("/")
        }
    }
    componentWillUpdate(nextProps) {
        if(! nextProps.isAuth) {this.context.router.push("/")            
        }
    }
    render() {
        return <div>Delete Document Page!!!</div>}}function mapstateToProps(state) {
    isAuth: state.auth
}
export default connect(mapStateToProps)(DelDocument)
Copy the code

Different pages have different functions, but most of their implementation logic is the same.

Actions in each component:

  • Connect to store state via react-redux.
  • willstate.authMapped to a componentprops.isAuthProperties.
  • incomponentWillMountTo check whether the user is authorized.
  • incomponentWillUpdateTo check whether the user is authorized.

Assuming our project extends more other components, we find that we implement the above actions in each component. This is gonna be boring.

We need to find ways to implement logic in only one place. The best way to do this is to use HOC.

To do this, we wrap all our logic into a function that returns a component:

function requireAuthentication(composedComponent) {
    class Authentication extends React.Component {
        componentwillMount() {
            if(!this.props.isAuth){
                this.context.router.push("/")
            }
        }
        componentWillUpdate(nextProps) {
            if(! nextProps.isAuth) {this.context.router.push("/")            
            }
        }
        render() {
            <ComposedComponent />
        }
    }
    function mapstateToProps(state) {
        isAuth: state.auth
    }
    return connect(mapStateToProps)(Authentication)
}
Copy the code

As you can see, we have encapsulated all the same logic in the Authentication component. The requireAuthentication function will connect the Authentication component to the store and return it. The Authentication component will then render the component passed in with the composedCompoennt parameter.

Our route now looks like this:

<Route path="/"component={App}> <Route path="/dashboard" component={requireAuthentication(Documents)}/> <Route path="document/:id/view"  component={requireAuthentication(ViewDocument)} /> <Route path="documents/:id/delete" component={requireAuthentication(DelDocument)} /> <Route path="documents/:id/edit" component={requireAuthentication(EditDocument)}/> </Route>Copy the code

So no matter how many routes our application will have in the future, we don’t have to worry about the logic of adding authentication to the component, we just call the requireAuthentication function and pass the component as a parameter.

There are many benefits to using HOC. When you find yourself repeating the same logic, you need to wrap the same logic together and use HOC.

conclusion

Higher-order functions:

  • Returns a function;
  • Can solve the DRY [1] problem.

React Advanced components:

  • Receive a component as a parameter;
  • Return another component;
  • The returned component will render the component passed by parameter;
  • Can solve the DRY [2] problem.

If you have any questions about this article, or if you think anything needs to be added, corrected or removed, please comment or email me.

Thank you very much!

If you find any mistakes in your translation or other areas that need to be improved, you are welcome to the Nuggets Translation Program to revise and PR your translation, and you can also get the corresponding reward points. The permanent link to this article at the beginning of this article is the MarkDown link to this article on GitHub.


The Nuggets Translation Project is a community that translates quality Internet technical articles from English sharing articles on nuggets. The content covers Android, iOS, front-end, back-end, blockchain, products, design, artificial intelligence and other fields. If you want to see more high-quality translation, please continue to pay attention to the Translation plan of Digging Gold, the official Weibo, Zhihu column.