React Design Patterns — Higher Order Components

Isha Jauhari
Feb 6 · 5 min read

What are Higher Order Component (HOCs)?

Higher order components comes under functional programming methodology.

It is a function that takes a component and returns a new component.
HOC is not a feature in React or any other programming language, but it is a design pattern evolved from the compositional( made of components ) over inheritance nature of react.

In simple terms, Higher-Order Component -

  • Is a component
  • Takes in a component as an argument
  • Returns a new component which can render the original component that was passed in.

They allow us to abstract not only just values but Action too.

function higherOrderComponent (Component) {
return class extends React.Component {
render() {
return <Component />
}
}
}

WHY, Where and How ?

The primary use of Higher-Order Component is to enhance the re-usability of particular components in multiple modules or components.
We can also comprise various components to get improved components. Many third party libraries are using this feature to write another cool library.

Example -
Adding new Property to a component.

// Take in a component as argument WrappedComponent
function enhance(WrappedComponent) {
// And return a new anonymous component
return class extends React.Component{
render(){
return <WrappedComponent
name = "Enhanced Component"
{...this.props} />;
}
}
}

Defining the component to send in, which uses the name prop…

const Hello = ({ name }) => <h1>It is {name}!</h1>;

Now enhancing it with HOC.

const EnhanceComponent = enhance(Hello);// No need to send again name prop, it is already sent in
// by the HOC. It will output “It is Enhanced Component!”
<EnhanceComponent/>

Some Popular Examples

There are many modules adopting the HOC design pattern as a means of injecting functionality from the module into your components — this has proven to be the most popular use case of HOCs.

  • connectfunction in React Redux. The connect function gives the access of global state in the Redux store to components, and it passes these values to the component as props.
connect(mapStateToProps, mapDispatchToProps)(UserPage);
  • In react-router library, we have withRouterto get access to the history object’s properties and the closest <Route>’s match. It injects the router information and functionality into the component, enabling the developer access or change the route.
withRouter(UserPage);
  • In react-css-modules library for customizing application styling, we have HOC CSSModules.
  • We can save and load cookies from any component using react-cookies, and function injection of that component is done via withCookies() HOC.
import { withCookies } from 'react-cookie';//// Some Piece of Code /////export default withCookies(Component);

Points to Consider

1. Usage of HOCs

We should not be using HOCs inside render method. Reason being is React’s Reconciliation algorithm uses component identity to check whether existing subtree needs to be updated or new component(/subtree) needs to be mounted on the DOM tree.

Based on the outcome from the diffing algorithm whether the components are identical(===) or not, React can take one of the below two approach -

  • Either recursively update the subtree
  • Or Un-mount the previous subtree and mount the new one.

In case of HOC, we shouldn’t apply a HOC to a component within the render method of a component:

render() {
// A new version of NewComponent is created on every render
// NewComponent1 !== NewComponent2
const NewComponent = enhance(ExistingComponent);
// That causes the entire subtree to unmount/remount each time!
return <NewComponent />;
}

Problem with above approach is two-fold. One is Performance and second is state loss of the component. Unmounting, re-mounting results in component’s state loss as well as its children’s state.

To avoid this, we should apply HOCs outside the component definition so that the resulting component is created only once. In this way, its identity will be consistent across renders. And this is what you want.

In case we want a dynamic HOC, we can implement this using component’s lifecycle methods or its constructor.

2. Handling of Static Methods

When we apply HOC to a component, static methods of the Existing component are not get copied over automatically.

// Define a static method
ExistingComponent.staticMethod = function() {/*...*/}
// Now apply a HOC
const NewComponent = doSomeStuff(ExistingComponent);
// The new component has no static method
typeof NewComponent.staticMethod === 'undefined' // true

There are multiple ways to manage static methods in case of HOC.

  • Copying static methods into new container before returning it. Using this, we should know what methods needs to be copied.

To copy all the methods in one go, we can use hoist-non-react-statics. It automatically copy all non-React static methods:

import hoistNonReactStatic from 'hoist-non-react-statics';function copyMethods(ExistingComponent) {
class NewComponent extends React.Component {/*...*/}
hoistNonReactStatic(NewComponent, ExistingComponent);
return NewComponent;
}
  • Another way is to export the static method separately from the component itself.
// Instead of...
ExistingComponent.someFunction = someFunction;
export default ExistingComponent;
// ...export the method separately...
export { staticFunction };
// ...and in the consuming module, import both
import ExistingComponent, { staticFunction } from './ExistingComponent.js';

3. Passing of Refs

We can’t pass ref type in HOC. That’s because ref is not really a prop — it is like key, which is handled specially by React.

In case of HOC, the ref will always refers to an instance of the outermost container component, not the wrapped component. This will be irrespective of the fact whether ref is added to an element whose component is the result of a HOC. We can resolve this issue by using React.forwardRef API.

Conclusion

HOC is a powerful concept that is used to enhance a component with new functions or data.

  • In HOC, we don’t modify or mutate the component. We create new ones.
  • Since it is a pure function, it has no side effects. It only returns a new component.
  • Used to share common functionality between components without repeating code.

There are still many things that I want to cover like different Use cases, how to debug them, etc, etc. I will try to write another article in future with all these details and will link to this one for better understanding of the same.

JavaScript in Plain English

Learn the web's most important programming language.

Isha Jauhari

Written by

Trying to learn new things every day and sharing my knowledge with people || Avid Reader

JavaScript in Plain English

Learn the web's most important programming language.

More From Medium

More from JavaScript in Plain English

More from JavaScript in Plain English

More from JavaScript in Plain English

5 Secret features of JSON.stringify()

More from JavaScript in Plain English

More from JavaScript in Plain English

7 really good reasons not to use TypeScript

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade