Higher order components in React

Mindaugas Nakrosis
3 min readMar 20, 2021

--

A higher-order component (HOC) is an advanced technique in React for reusing component logic. HOC is not part of the React API. It is a pattern that emerges from React’s compositional nature and that can be reused in different programming languages as well.

Photo by Didssph on Unsplash

Concretely, a higher-order component is a function that takes a component and returns a new component.

const CoolerComponent = higherOrderComponent(NotAsCoolComponent);

Let’s say you have a component that fetches and shows a list of dogs from some API source:

Photo by Anoir Chafik on Unsplash
class DogsList extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.state = {
// "APIDataSource" is some global api data source
dogs: APIDataSource.getDogs()
};
}

componentDidMount() {
APIDataSource.addChangeListener(this.handleChange);
}

componentWillUnmount() {
APIDataSource.removeChangeListener(this.handleChange);
}

handleChange() {
// Update component state whenever the data source changes
this.setState({
dogs: APIDataSource.getDogs()
});
}

render() {
return (
<div>
{this.state.dogs.map((dog) => (
<Dog dog={dog} key={dog.id} />
))}
</div>
);
}
}

Later you need to create a component that fetches a cute cat from API and shows it in a text block:

Photo by Joe Cleary on Unsplash
class CuteCat extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.state = {
cuteCat: APIDataSource.getCuteCat(props.id)
};
}

componentDidMount() {
APIDataSource.addChangeListener(this.handleChange);
}

componentWillUnmount() {
APIDataSource.removeChangeListener(this.handleChange);
}

handleChange() {
this.setState({
cuteCat: APIDataSource.getCuteCat(this.props.id)
});
}

render() {
return <TextBlock text={this.state.cuteCat} />;
}
}

DogsList and CuteCat aren’t identical — they call different methods on APIDataSource, and they render different JSX. However, some of their functionalities overlap:

  • On mount, add a change listener to APIDataSource.
  • Inside the listener, call setState whenever the data source changes.
  • On unmount, remove the change listener.

We can write a function that creates components, like DogsList and CuteCat, that subscribe to APIDataSource. The function will accept as one of its arguments a child component that receives the subscribed data as a prop. Let’s call the function withDataSubscription:

const DogsListWithSubscription = withDataSubscription(
DogsList,
(APIDataSource) => APIDataSource.getDogs()
);

const CuteCatWithSubscription = withDataSubscription(
CuteCat,
(APIDataSource, props) => APIDataSource.getCuteCat(props.id)
);
function withDataSubscription(WrappedComponent, selectData) {
// returns another component
return class extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.state = {
data: selectData(APIDataSource, props)
};
}

componentDidMount() {
// ... that takes care of the subscription...
APIDataSource.addChangeListener(this.handleChange);
}

componentWillUnmount() {
APIDataSource.removeChangeListener(this.handleChange);
}

handleChange() {
this.setState({
data: selectData(APIDataSource, this.props)
});
}

render() {
// Renders the wrapped component with data
// We pass through any additional props
return <WrappedComponent data={this.state.data} {...this.props} />;
}
};
}

And that’s it! Go on to my other article to find out HOCs I use the most.

--

--

No responses yet