Using ReactJS HoC pattern! Be aware of the side effects!

Before reading this story make sure that you are aware of ReactJS patterns. Let’s start from the basics.

What’s a Higher-Order Component ?

A higher-order component (HoC) is an advanced technique in React for reusing component logic. They are a pattern that emerges from React’s composition nature.

In a simple sentence, they are just JavaScript functions that take a component as an argument and returns a new enhanced component.

You can refer to official docs for more detail .

Let’s look into a simple HoC example.

import React from 'react';const nameHoC = (WrappedComponent) => {
return class extends React.Component {
constructor(props) {
super(props);
this.state = {
name: ""
};
}

componentDidMount() {
//Here we can fetch the data by calling an API
//and then set the state
//here I have used static data
this.setState({name: "Michael"});
}

render() {
const {name} = this.state;
return(
<div>
<WrappedComponent{...this.props} name={name}/>
</div>
)
}
}
};
export default nameHoC;

Here nameHoC is a function which is taking WrappedComponent as an argument and returning a brand new component by adding some new features to WrappedComponent. We are passing name as a props to WrappedComponent.

Now, let’s use this HoC.

import React from 'react';
import nameHoC from './nameHoC';
class WishMe extends React.Component {
constructor(props) {
super(props);
}

render() {
const { name } = this.props;
return(
<div>
<h1>Hello {name}! Good morning</h1>
</div>
)
}
}

export default nameHoC(WishMe);

As you can see here, we have imported nameHoCand wrappedWishMe component in it, nameHoC(WishMe). In WishMe we are using data from props to get the name of the person to wish. Below is the output.

Note*: When we are callingnameHoC(WishMe) , this means composition with this HoC will be happened only once at compile time. We can say, it is a static composition.

Have you used it before ?

  • If you have used react with redux then you must have used connect function. This is one example of the HoC.
  • In react-router library, we have withRouter to get access to the history object's properties and the closest <Route>'s match.
  • If you have got chance to use react-css-modules library for customizing the styling of your application, then you must be aware of HoC CSSModules.

Using multiple HoCs ?

Now we are going to use multiple HoCs. Let’s take above example, in WishMe component we have hard coded the wish “Good morning”. We want this message to be dynamic, let say, it should be according to time. So, we can have another HoC which will have this logic.

import React from 'react';const withWishesHoC = (WrappedComponent) => {
return class extends React.Component {
constructor(props) {
super(props);
this.state = {
message: ""
};
}

componentDidMount() {
//Here we can take the time
//and build the message according to time
//then set the state with message
//here I have used static data
this.setState({message: "Good morning"});
}

render() {
const { message } = this.state;
return(
<div>
<WrappedComponent{...this.props} message={message}/>
</div>
)
}
}
};
export default withWishesHoC;

Here we have created another HoC withWishesHoC which is passing message in a props. Let’s use this HoC.

import React from 'react';
import nameHoC from './nameHoC';
import withWishesHoC from './withWishesHoC';
class WishMe extends React.Component {
constructor(props) {
super(props);
}

render() {
const { name, message } = this.props;
return(
<div>
<h1>Hello {name}! {message}</h1>
</div>
)
}
}

export default withWishesHoC(nameHoC(WishMe));

We got below result in the browser.

What we should aware of while using multiple HoCs ?

Main use of HoCs is to achieve reusability. Typically, developers pull out reusable codes into a higher order component (HoC). It involves passing props and hoisting statics. It can leads to collisions, in simple words, we can get props name conflicts, especially when we are composing a component with multiple HoCs that might use the same prop names.

Here, withWishesHoC and nameHoC are simple HoCs and we know what all props passed in wrapped component. Because we have design HoCs. Let’s pass the values from two different HoCs with the same props name “data”.

From nameHoC :

<div>
<WrappedComponent{...this.props} data={name}/>
</div>

From withWishesHoC :

<div>
<WrappedComponent{...this.props} data={message}/>
</div>

Then data from withWishesHoC will be overridden by data from nameHoC . Even we will not get any error or warning in the browser console by react.

Good morning message is overridden by Michael

If we are using HoCs from third party then we don’t have any control over it’s implementations. We don’t know what names are being used for props. With multiple HoCs composing a component, it also becomes harder to determine which props come from which HoCs and ensuring you’re handling all of them in your component correctly.

What is the solution to this problem ?

Render Props ?

Yes, exactly. Using render props for composition happens dynamically during render at run time, letting us take advantage of the full React life cycle.

Let see a tweet from Michael Jackson.

Render props is like “you give me data, I will tell you what to render.”

A render prop is a function prop that a component uses to know what to render.

How can we do it with above example ?

Lets rewrite both HoCs nameHoC and withWishesHoC as a simple react component MyName and WithWishes .

import React from 'react';class MyName extends React.Component {
constructor(props) {
super(props);
this.state = {
data: ""
};
}

componentDidMount() {
//Here we can fetch name by calling an API
//and then set the state
this.setState({data: "Michael"});
}

render() {
return(
<div>
{this.props.render(this.state)}
</div>
)
}
}

export default MyName;
class WithWishes extends React.Component {
constructor(props) {
super(props);
this.state = {
data: ""
};
}

componentDidMount() {
//Here we can take the time
//and build the message according to time
//then set the state with message
this.setState({data: "Good morning"});
}

render() {
return(
<div>
{this.props.render(this.state)}
</div>
)
}
}
export default WithWishes;

If you can notice here, in both of the components I have used the same name in the state i.e data. Inside render methods I am returning {this.props.render(this.state)} . I am calling render by passing component’s state. Let see, how to use these.

import React from 'react';
import MyName from './MyName';
import WithWishes from './WithWishes';

class WishMe extends React.Component {

render() {
return(
<div>
<MyName render=
{nameData=>(
<WithWishes render=
{messageData=> (
<h1>Hi {nameData.data}! {messageData.data}</h1>
)}
/>
)}
/>
</div>
)
}
}

export default WishMe;

Although we have same state names “data” but we will not face any conflict issues. We will get the result as expected.

We have not done any static composition here. React is known for it’s dynamic composition. We have maintained the spirit.

Conclusion

I am not against the HoC at all. What to use and when to use, It depends on the situation and your needs. So keep learning and experimenting. Doubtless, there will be different and better patterns as the web and React move forward.

Note*: My example is not a real time example. I used this simple example only to make you understand the concept and address the problem. Please don’t debate on it.

Please checkout my blog where I have covered render props in detail with proper example.

This story is highly inspired by Michael Jackson ‘s Phoenix ReactJS meetup talk “Never Write Another HoC”. He nicely put up his thoughts.

Fullstack web developer, www.gobindathakur.com