Higher Order Components (HOC) are a direct translation of Higher Order Functions from functional languages. A HOC extends the state / behavior of the inner component in a composable way, so you can add many more extensions without the knowledge of the inner component.
React is particularly suited to support this with minimum friction. The tag structure of JSX also helps in visualizing this in text.
In this blog post, we’ll take a look at many of the use-cases for HOCs. They are all from real projects so you are assured of its practicality!
HOCs can be created in couple of ways:
class
, rendering the inner-component via props.children
props.children
props.children
@decorator
language extensionThe code below shows these ways of constructing an HOC for a Guard Component (which we will cover in the next section).
import React from 'react';
// 1. Wrapper function returning a class
export function guardedComponentFunction(condition, Component) {
return class Guarded extends React.Component {
render() {
return condition ? <Component {...this.props} /> : null;
}
};
}
// 2. Wrapper function
export const GuardedComponent = ({ condition, children }) => {
return condition ? children : null;
};
// 3. Class that wraps the component via props.children
export class GuardedComponentClass extends React.Component {
static get defaultProps() {
return {
condition: true,
};
}
static get propTypes() {
return {
condition: React.PropTypes.bool,
};
}
render() {
return this.props.condition ? this.props.children : null;
}
}
// 4. As a @decorator
function guardWith(condition) {
return function(Component) {
return guardedComponentFunction(condition, Component);
};
}
@guardWith(true)
class ComponentToGuard extends React.Component {
render() {
return <h2>Advanced Admin Component</h2>;
}
}
Guard components are most useful when you want to render a component only if a certain condition matches. For example, if you have the Admin area which should only be visible to logged-in admin users, you can protect it with a Guard component. Other names for this type of component are Protected
or Conditional
or Toggle
.
This is an extension of the Guard component and adds the ability to handle both true and false conditions. You can also treat this as a Toggle
wrapper that shows one or the other depending on the condition. I’ve used this in cases where I’ll show the list of items when the number of items are > 0 and an empty message when = 0.
The code below shows the use of an IfElse
component. It is fairly simple and uses the first child as the “true” component and the second one as the “false” component.
// if-else.jsx
import React from 'react';
export function IfElse({ condition, children }) {
const childrenArray = React.Children.toArray(children);
const trueChild = childrenArray[0],
falseChild = childrenArray[1] || null;
return condition ? trueChild : falseChild;
}
// Somewhere in the app
import { IfElse } from './if-else';
import { ListOfItems, EmptyList } from './list-components';
class SomeAppComponent extends React.Component {
// ...
render() {
const { items } = this.props;
return (
<IfElse condition={items && items.length > 0}>
<ListOfItems items={items} />
<EmptyList message="There are no items in the list" />
</IfElse>
);
}
// ...
}
Note: You can also extend the IfElse
component to be more general with a SwitchCase
component! I’ll leave that as a reader exercise :-)
If you feel more adventurous, you can even create looping-constructs as HOCs! Think WhileComponent
, ForComponent
, etc.
Provider
components (or wrapper functions) allow you to mixin behavior and state and make it available as props on the wrapped component.
If you have used Redux or MobX, the connect()
and observer
, respectively, work as a Providers (or wrappers). They abstract the details about the connection to the store and make it available as props on the wrapped inner component.
The React-Router is yet another example where a Provider component (RouterContext
) takes care of instantiating the inner component(s) and passing the Router details.
The provider component is probably the most versatile of all HOC and can do a variety of things like:
Provide JSFiddles for the examples above.
If you already have examples of the above, I will be happy to link it here.
HOCs are a powerful concept derived from functional languages. It allows you to create composable components that abstract details and make the component tree more declarative. Hope the examples above give you some ideas to extend, discover your own patterns of HOC in your application.