Higher Order What?

K - Jan 6 '17 - - Dev Community

So, since it's 2017 you probably using one of these fancy new component based frameworks, like React and you read about all this Higher Order Component (HOC) talk last year, but you still don't understand what it's all about. You maybe read about these Higher Order Functions (HOF) and ask yourself, what these pesky functional programmers up to. Are they trying to ravage a perfectly object oriented concept again? I can assure you, they mean you no harm... this time!

For OOP people to understand, HOCs are an alternative to inheritence. They allow you to do composition in a dynamic way.

// Example adding basic HTTP request functions to a component via inheritence

class HttpConnectedComponent extends Component {...}

class UserList extends HttpConnectedComponent {...}

class Application {
  render() {
    return <UserList/>
  }
}
Enter fullscreen mode Exit fullscreen mode

The problem here is obvious, you now got a new base class and always have to extend it if you want to have HTTP functionallity, if you got a bunch of other base classes it gets messy rather quick and your component is now tighly coupled to the base class.

HOCs allow you to evade this problem. They are simply functions that take the definition of a component and return a new definition of a component. This can be a bit ... puzzling, I know, but bare with me.

Lets go to HOFs first, they are rather simple. They are functions that take functions as arguments and/or return functions.

 // This HOF takes a function and creates a new one, that will return a 
 // stringified version of the first functions result.

 function stringifyResult(f) {
   return function (arg) { f(arg).toString() }
 }

 function timesTwo(num) { return num * 2 }

 const timesTwoAsString = stringifyResult(timesTwo)

 timesTwo(4) // -> 8
 timesTwoAsString(4) // -> "8"
Enter fullscreen mode Exit fullscreen mode

As you can see in the function definiton of stringifyResult, you could also modify the arguments that are passed to f, not just the result of f. So you could do a variety of behvaiour extensions to any function with this pattern.

The idea with HOCs is now, to do this with components. The point is, that you end up with a enhanced version of your basic component, without extending some other base classes and you can simply use this new version everywhere in your app, if needed. Or you could use it at specific points in your app, to modify the behaviour of a component depending on the context, without handling this behavior change in the component itself (authentication/authorization, etc).

You could, for example, code a UserList component that gets an array of users as prop (in the case of React) and then enhance this with a connectHttp HOC, that will "wrap" the UserList in another component, that will request the user data from the server and pass it into the right prop on completion.

// React Example

// This function creates a new class definition on every call
// It takes an URL, a prop name and a Component class(!) not an object.
function connectHttp(url, prop, Comp) {

  return class extends Component {

    state = {result: null}

    // When it's in the DOM, get the data
    componentDidMount() {
      getData(url).then(result => this.setState({result}))
    }

    // render a placeholder while the data is null
    // render the Component with the data in the right prop, when its not null
    render() {
      const {result} = this.state
      // creating a new props object, that passes the props of "this" component 
      // through AND adds the data of the request
      const props = {...this.props, [prop]: result}

      return result? <Comp {...props}/> : <span>Loading...</span>
    }

  }

}

// Simple user list
// Can be tested stand-alone
// Is completely oblivious to the "source" of the data
function UserList({users=[]}) {
  return (
    <ul>
      {props.users.map(user => <li>{user.name}</li>)}
    </ul>
  )
}

// The wrapped component class
// Can now be used to create lists, that fetch their data themself
const ConnectedUserList = connectHttp('/users', 'users', UserList)

...

<Application>
  <ConnectedUserList/>
</Application>
Enter fullscreen mode Exit fullscreen mode

In this example, the HOC allows you to enhance other component definitions dynamically. It creates a central point that handles the HTTP connection and the other components can be much simpler, which eases testing and refactoring. You could write a new HOC, that gets the data via WebSockets and none of your other components will be the wiser.

This behaviour could also be achieved with a "regular" component, that simply passes data down to its child components, whatever they are. The difference is, that you would have to explicitly wrap your component every time, which can be rather cumbersome in bigger applications.

Wrapping components like this is only one way to use HOC, another way is to "extend" your target component. The new component inherits from your component instead of your component inherting from the new one. This gives you more control over the components behaviour. You could override (lifecycle) methods for example. This also flattens the component hierarchy, because just "one" component is constructed and not the wrapper and the target component.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .