Fetching Data with a Higher Order Component in React

Let us say you are building a sick application in React, and Redux is managing your application’s data. Maybe not Redux, you could also be using React’s new context api. Regardless, there are times when waiting for data can get really screwy. For instance, maybe you need page specific data to load a certain UI, or in some cases the page just doesn’t look right until fetching is completed. That really blows.

Higher order components could relieve some grief in these situations, but what are they? To put it simply, you wrap a higher order component around the export of a normal component, like you have been doing with connect to work with Redux. How do you make a higher order component? In my example we will assume we need the data of a user, and we are identifying that user by an ID in the URL. We will also assume the application is driven by react-router, and that match is available as a property.

When you are wrapping a component with a HOC, it feels like you’re just entering your component as an argument to a function. So lets start there:

export default function withUser (WrappedComponent) {
}

Now we can drop a React component in there, and connect it to redux.

import React from 'react'
import { connect } from 'react-redux'
export default function withUser (WrappedComponent) {
class With extends React.Component {
}
  const mapStateToProps = (state) => {
return {
users: state.users
}
}
  return connect(mapStateToProps)(With)
}

Lookin’ good. Now we need it to render something so the compiler stops screaming at us. For this section, we are going to want to render null when we do not have the data we need.

import React from 'react'
import { connect } from 'react-redux'
export default function withUser (WrappedComponent) {
class With extends React.Component {
render () {
const {
users,
match
} = this.props
      if (!users.hasOwnProps(match.params.id)) {
return null
}
      return <WrappedComponent {...this.props} />
}
}
  const mapStateToProps = (state) => {
return {
users: state.users
}
}
  return connect(mapStateToProps)(With)
}

Now we need to actually grab the data, we will want to use componentDidMount for this, and we need to hook up our redux actions to this new component.

import React from 'react'
import { connect } from 'react-redux'
import { bindActionCreators } from 'react'
import * as usersActions from '../usersActions'
export default function withUser (WrappedComponent) {
class With extends React.Component {
componentDidMount () {
const {
match
} = this.props
      this.props.usersActions.get(match.params.id)
}
    render () {
const {
users,
match
} = this.props
      if (!users.hasOwnProperty(match.params.id)) {
return null
}
      return <WrappedComponent {...this.props} />
}
}
  const mapStateToProps = (state) => {
return {
users: state.users
}
}
  const mapDispatchToProps = (dispatch) => {
return {
usersActions: bindActionCreators(usersActions, dispatch)
}
}
  return connect(mapStateToProps, mapDispatchToProps)(With)
}

That should be it! If you want to get fancier you could replace null with a component that displays the page loading (like a spinner or something), but for now at least our UI will stop getting wacky while the browser is waiting for data.