Higher-Order Components and Data Fetching in React

Higher-Order Components and Data Fetching in React

In July last year I decided to write new code for Klart.co (a bookmarking tool for designers) using React. I've bumped into some issues since then, but the overall experience has been great. There's a lot of stuff written about how to write React applications but I think Higher-Order components (HOCS) are often left out or poorly explained, so I'll give it a go here.

A common pattern when using React to build web applications is to:

  1. Fetch some data and show spinner
  2. Show data or error message

This is often done using componentWillMount, componentWillReceiveProps and constructor methods. In this post I will describe a nice little pattern using HOCS to avoid code repetition and promote consistency in your application.

Hello Higher-Order Components

Higher-Order components are simply components that takes a component as a parameter and returns another component. This is pretty useful since we can encapsulate common logic or even views in this component. Let's look at a simple example used to show a fixed sidebar alongside our component:

const withSidebar = WrappedComponent => props =>
  <div className="has-sidebar">
    <SideBar />
    <WrappedComponent {...props} />
  </div>

And we can use it like this:

import withSideBar from './withSidebar';
import Blog from './Blog';

const BlogAppWithSidebar = () => <Blog />

export default withSideBar(BlogAppWithSidebar);

That's about as simple as it gets. I'm using the Object spread syntax to pass properties to the wrapped component. While this example is very basic, HOCS are a very powerful paradigm and allows for flexible composition. Let's look at how we can use this to fetch data for our components by defining a component withData.

const withData = WrappedComponent =>
  class extends React.Component {
    componentWillMount() {
      // Don't reload data when not needed
      if ((this.props.data && this.props.data.length) || !this.props.fetchData) return;
      this.props.fetchData();
    }

    componentWillReceiveProps(nextProps) {
      // Only fetch new data if resource has changed
      const prevId = this.props.resourceId;
      const thisId = nextProps.resourceId;
      if (prevId !== thisId && thisId) this.props.fetchData(thisId);
    }

    render() {
      const { isFetching } = this.props;
      if (isFetching) return <Spinner />;
      return <WrappedComponent {...this.props} />;
    }
  };

export default withData;

We can compose as many HOCS as we want. For example we can combine withSidebar and withData like this:

import withData from './withData';
import Blog from './Blog';

const BlogAppWithData = () => <Blog />

export default withSidebar(withData(BlogAppWithData));

And use it like this:

import BlogAppWithData from './Blog

const App = () =>
  <BlogAppWithData data={someData} fetchData={someFunc} isFetching={someBool} />;

export default App;

This will render our app with a Sidebar and a Spinner while fetching data. When the data is ready it will render our Blog. We can use these HOCS to render just about anything that needs data fetching and keep it DRY. 👏

A final word

HOCS are great but no silver bullet. Don't over-architect your application. I find that it makes it easy to have consistent UI and avoid code repetition and I use it a lot on Klart.co. But that's me. 🤓