Learning Higher Order Components in React.js

Justin Langlinais
Level Up Coding
Published in
6 min readOct 4, 2020

--

Photo by Wojciech Portnicki on Unsplash

Higher-Order Components are a technique used in React that allows us to reuse component logic. An example of this would be a component that renders conditionally based on whether the user is logged in.

More specifically, a Higher-Order Component is a function that takes a component as an argument and returns a component.

For this walkthrough, let’s build a simple React app that will use a Higher-Order Component to protect a page, requiring someone to be logged in to view it. We will control the logged in/out status with a button, so we can focus on the HOC. Then, we’ll create another HOC to protect a page based on whether our API or database content is loaded, and we’ll nest the HOCs! To get started, let’s create a new React project:

#=> npx create-react-app learning-hoc
#=> cd learning-hoc
#=> code .

Let’s clean out App.js and change it into a class component. We’re changing it so that we can use state here in a simple way for our walkthrough. When we’re done, it looks like this:

// App.jsimport React from 'react';
import './App.css';
class App extends React.Component {
render () {
return (
<div className="App">

</div>
)
}
};
export default App;

Next, let’s quickly build a component to hold our content we’d like to protect. For the purposes of this walkthrough, it’ll be super simple:

#=> cd src
#=> mkdir components
#=> cd components
#=> touch Content.js

In the Content.js file, we’ll set up a simple class component that returns our super secret protected information, wrapped in an <h1> tag:

// ./components/Content.jsimport React from 'react';class Content extends React.Component {
render () {
return (
<h1>Super Secret Protected Information</h1>
)
}
};
export default Content;

Let’s import this into App.js and use npm start to make sure we can see it:

// App.jsimport React from 'react';
import './App.css';
import Content from './components/Content.js'
class App extends React.Component {
render () {
return (
<div className="App">
<Content />
</div>
)
}
};
export default App;

Cool, we can see our super secret information! But, how can we control access to this, requiring that a user is logged in to see it?

Let’s build our Privacy Higher-Order Component, which will verify that the user is logged in before rendering whatever component we pass through it. For us, that will be <Content />. So, let’s create a new folder in /src/called HOCs, and then create our PrivacyHOC.js file in that folder:

#=> cd src
#=> mkdir HOCs
#=> cd HOCs
#=> touch PrivacyHOC.js

Great! Let’s start building our Higher-Order Component! We’ll start by setting up the skeleton:

// ./HOCs/PrivacyHOC.jsimport React from 'react';export default function PrivacyHOC(WrappedComponent) {
return (
class PrivacyHOC extends React.Component {
render () {
return <WrappedComponent />
}
}
)
};

Let’s take a moment to think about what we’re doing above. Our function, PrivacyHOC, takes a component as an argument. Then, it returns that component. Simple enough for now, right?

So, how do we wrap our Content component with this PrivacyHOC component? Let’s hop back over to Content.js and change our export statement. Instead of exporting Content, we will export our PrivacyHOC function with Content passed through it:

// ./components/Content.jsimport React from 'react';
import PrivacyHOC from '../HOCs/PrivacyHOC.js'
class Content extends React.Component {
render () {
return (
<h1>Super Secret Protected Information</h1>
)
}
};
export default PrivacyHOC(Content);

Cool! Check the browser to make sure that it’s displaying the super secret information! Alright, now we need build out some logic that will look to see whether we are logged in or not before displaying Content. Let’s think about how we want to do this…

First, in App.js, we want to track login status with a boolean in our state, then pass that down as props to the Content component. Because Content is wrapped in PrivacyHOC through Content’s export statement, PrivacyHOC will have access to props passed down to Content. We can use that to conditionally render Content based on the login status. We can add more complexity later, but let’s set this up to make sure it works:

// App.jsimport React from 'react';
import './App.css';
import Content from './components/Content.js'
class App extends React.Component {
state = {
loggedIn : true
}

render () {
return (
<div className="App">
<Content loggedIn={this.state.loggedIn} />
</div>
)
}
};
export default App;

Above, we’ve added loggedIn to our state at App level, and set it to true. We’ve also passed that value down to Content (and subsequently PrivacyHOC), so we have access to it in those components through props. Let’s set up our conditional rendering in PrivacyHOC:

// ./HOCs/PrivacyHOC.jsimport React from 'react';export default function PrivacyHOC(WrappedComponent) {
return (
class PrivacyHOC extends React.Component {
isLoggedIn = () => {
return this.props.loggedIn
}

render () {
return this.isLoggedIn() ?
<WrappedComponent {...this.props} /> :
<h1>You must be logged in</h1>

}
}
)
};

Above, we have a function, isLoggedIn, that returns the value of this.props.loggedIn, which is being passed down from App’s state. In our render of the returned component, we’re using a ternary statement based on the value of this.isLoggedIn. If it’s true, it’ll return and render <WrappedComponent />, which in this case is Content. If it’s false, it’ll tell us we must be logged in.

Note that we have added {…this.props} into our <WrappedComponent />. This will pass down and spread all props so we have access to them in the component. This is especially important when we nest an HOC inside of another one.

So, because ourApp’s state.loggedIn is set to true, we should see Content. Pop back over to your browser to verify. If that’s working, change state.loggedIn to false, and then check it again. It should tell us we must be logged in.

Hey, guess what? You just built and implemented a Higher-Order Component! I’d love to give you a high five right now. Let’s ride this wave and build out our project a little more. Let’s make a button that toggles our login status, so we can see the change in real time. After that, we’ll make another HOC to control what’s rendered based on whether our content is loaded from our database or an API.

Let’s start with a button in App.js that will toggle its state.loggedIn boolean value. We’ll also display the current value of state.loggedIn on the screen:

// App.jsimport React from 'react';
import './App.css';
import Content from './components/Content.js'
class App extends React.Component {
state = {
loggedIn : true
}
render () {
return (
<div className="App">
{this.state.loggedIn ? <h2>You are logged in!</h2> : <h2>You are logged out :(</h2>}
<button onClick={() => this.setState({loggedIn : !this.state.loggedIn})}>
Toggle Login/Logout
</button>

<Content loggedIn={this.state.loggedIn} />
</div>
)
}
};
export default App;

Above, we’ve set up a ternary statement that, based on the value of this.state.loggedIn, will either tell us we’re logged in or we’re logged out. Below that is a button that toggles the value of this.state.loggedIn. Now, we can see in real time how our PrivacyHOC controls what’s displayed on screen!

To wrap this all up, let’s now build a LoaderHOC to control what’s rendered based on whether our content is loaded. This way, we can have something on screen for the user to see while our API or database loads and renders, instead of hanging and running the risk of our user moving along. The LoaderHOC will look almost identical to the PrivacyHOC:

// ./HOCs/LoaderHOC.jsimport React from 'react';export default function LoaderHOC(WrappedComponent) {
return (
class LoaderHOC extends React.Component {
isLoaded = () => {
return this.props.loaded
}
render () {
return this.isLoaded() ? <WrappedComponent {...this.props} /> : <h1>Content loading...</h1>
}
}
)
}

Above, just like the PrivacyHOC, we’re taking a component as an argument and using a ternary to check the status of App’s state.loaded to either display our Content or display a message that the content is loading.

Let’s wire this up over in App.js:

// App.jsimport React from 'react';
import './App.css';
import Content from './components/Content.js'
class App extends React.Component {
state = {
loggedIn : true,
loaded : false
}
render () {
return (
<div className="App">
<div>
{this.state.loggedIn ? <h2>You are logged in!</h2> : <h2>You are logged out :(</h2>}
<button onClick={() => this.setState({loggedIn : !this.state.loggedIn})}>
Toggle Login/Logout
</button>
</div>
<div>
{this.state.loaded ? <h2>Content loaded</h2> : <h2>Content loading...</h2>}
<button onClick={() => this.setState({loaded : !this.state.loaded})}>
Toggle Content Loaded
</button>
</div>

<Content loggedIn={this.state.loggedIn} />
</div>
)
}
};
export default App;

Our last step is to nest our LoaderHOC within our PrivacyHOC in the Content.js export statement:

// ./components/Content.jsimport React from 'react';
import PrivacyHOC from '../HOCs/PrivacyHOC.js'
import LoaderHOC from '../HOCs/LoaderHOC.js'
class Content extends React.Component {
render () {
return (
<h1>Super Secret Protected Information</h1>
)
}
};
export default PrivacyHOC(LoaderHOC(Content));

Now, PrivacyHOC controls our Content, whether it’s loaded or not! Pretty cool!

As our App grows, we can use these HOCs for any component we’d like — all we have to do is pass our component through the HOCs in that component’s export statement!

I hope this helped you understand Higher-Order Functions a little better, and I’d love to hear your experiences or ideas!

--

--