Real world Higher-Order Components (HOCs)

Trying to wrap your head around React HOCs? Want some real world examples to look at?

Then you’ve come to the right place!

If you are completely new to React HOCs, you should read my Simple explanation of Higher-Order Components (HOC) before moving on.

 

Add a hidden prop to your components

This HOC adds a hidden prop to your components. When hidden is set to true, your components becomes… hidden!

Usage

const HelloWorldWithHidden = withHiddenProp(HelloWorld);

// ...

<HelloWorldWithHidden hidden={true}/>

HOC

export function withHiddenProp(WrappedComponent) {
    return class extends React.Component {
        render() {
            if (this.props.hidden) {
                return null;
            } else {
                return <WrappedComponent
                  {...this.props} />;
            }
        }
    };
}

Full example

Full example on codesandbox.io

 

Add toggle functionality to your components

This HOC adds two props to your components: toggled and onToggle

  • toggled: a boolean that represents the toggle
  • onToggle: a function that toggles the toggled variable

Usage

const Button = ({ toggled, onToggle }) =>
  <button onClick={onToggle}>
    {toggled ? "It's ON! :)" : "It's OFF! :("}
  </button>;

const ToggledButton = withToggle(Button);

// ...

<ToggledButton />

HOC

function withToggle(WrappedComponent) {
  return class extends React.Component {
    constructor(props) {
      super(props);
      this.onToggle = this.onToggle.bind(this);
      this.state = {
        toggled: false
      };
    }
    onToggle() {
      this.setState({ toggled: !this.state.toggled });
    }
    render() {
      return (
        <WrappedComponent
          onToggle={this.onToggle}
          toggled={this.state.toggled}
          {...this.props} />
      );
    }
  };
}

Full example

Full example on codesandbox.io

 

Show component when feature toggle is set

Feature toggles are a powerful technique to change behavior without deploying new code.

When using this HOC, your component will only be displayed when its feature toggle is set to true.

Usage

const HelloWorldWithToggle = whenFeatureToggled("helloWorld", HelloWorld);
// ...

<HelloWorldWithToggle/>

HOC

// This might come from a global config file 
const featureToggles = {
 helloWorld: true
};

function whenFeatureToggled(feature, WrappedComponent) {
  return class extends React.Component {
    render() {
      if (!featureToggles[feature]) {
        return null;
      } else {
        return <WrappedComponent {...this.props} />;
      }
    }
  };

Full example

Full example on codesandbox.io

 

Add a loading spinner while fetching data

A common UI pattern is showing a loading spinner while data is fetched asynchronously from the backend.

This HOC shows a spinner when this.props.data is null or undefined.

Usage

// Presentational component
const HelloWorldWithLoadingSpinner = withLoadingSpinner(HelloWorld);

// Conatiner component
class HelloWorldContainer extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      data: null
    };
    // In the real world, this is probably an Ajax call
    setTimeout(() => this.loadingComplete(), 2000);
  }
  loadingComplete() {
    this.setState({ data: "hello" });
  }
  render() {
    return <HelloWorldWithLoadingSpinner data={this.state.data} />;
  }
}

HOC

function withLoadingSpinner(WrappedComponent) {
  return class extends React.Component {
    render() {
      if (!this.props.data) {
        return <Spinner/>;
      } else {
        return <WrappedComponent {...this.props} />; 
      }
    }
  };
}

Full example

Full example on codesandbox.io

 

Responsive components

If you need a responsive design you have two options: either use media queries with CSS or use JavaScript/React. There are pro and cons for each. If you go for the React solution you might want to use this HOC.

This HOC is based on this excellent solution for detecting mobile/desktop by Gosha Arinich

Usage

class HelloWorld extends React.Component {
  render() {
    return this.props.isDesktop ? <h1>Hello world from Desktop</h1> : <h1>Hello world from mobile</h1>;
  }
}

const HelloWorldResponsive = detectDesktopOrMobile(HelloWorld);

// ...

<HelloWorldResponsive/>

HOC

function detectDesktopOrMobile(WrappedComponent) {
  return class extends React.Component {
    constructor() {
      super();
      this.state = {
        width: window.innerWidth,
      };
    }
    componentWillMount() {
      window.addEventListener('resize', this.handleWindowSizeChange);
    }

    // make sure to remove the listener
    // when the component is not mounted anymore
    componentWillUnmount() {
      window.removeEventListener('resize', this.handleWindowSizeChange);
    }

    handleWindowSizeChange = () => {
      this.setState({ width: window.innerWidth });
    };
    render() {
      return <WrappedComponent isDesktop={this.state.width > 500} {...this.props} />;
    }
  };
}

Full example

Full example on codesandbox.io 

 

Combining HOCs

Now we have a bunch of small composable HOCs. We can combine many HOCs on one component like this:

const Button = ({ toggled, onToggle }) =>
  <button onClick={onToggle}>
    {toggled ? "It's ON! :)" : "It's OFF! :("}
  </button>;


// Show a toggle button when it's feature toggled in
const ToggledButton = whenFeatureToggled("myToggleButton", withToggle(Button));

// ... 

<ToggledButton />

As you can see, you combine HOCs by doing nested function calls. This can be a bit tricky to read and work with when having many HOCs:

hoc1(hoc2(hoc3(hoc4(Component))))

A solution to this is using a utility library included in Redux (and Recompose) that composes HOCs:

compose(
  hoc1,
  hoc2,
  hoc3,
  hoc4
)(Component)

More readable and easier to work with!

  • zalinn

    About “loading spinner while fetching data”, the example is a little too simply and not specially reusable.
    How do you handle it when your data is fetching in the wrapped component ? (in its componentDidMount)
    I would like to have a reusable HOC for each component which fetch data. And the data are retrieving through an API.

    • jakob

      The example shows a HOC for a spinner, but it doesn’t show a HOC for fetching data as you point out. The HOCs in the article are for inspiration. Hopefully got inspired to write your own data fetching HOC! 🙂 Let me know if you struggle to finish it and I can help.

    • jakoblind

      My answer seem to have disappeared. I paste it here again:

      The example shows a HOC for a spinner, but it doesn’t show a HOC for fetching data as you point out. The HOCs in the article are for inspiration. Hopefully got inspired to write your own data fetching HOC! 🙂 Let me know if you struggle to finish it and I can help

  • What’s the difference between using compose and doing something like this?

    Component = hoc1(Component);
    Component = hoc2(Component);
    Component = hoc3(Component);
    Component = hoc4(Component);

    export default Component;

    I know it’s longer but does compose bring any advantage other than a terser syntax?

    • jakoblind

      In your solution you mutate a variable. Mutation and working with mutable data has many disadvantages to immutability. In your example I would say the disadvantage is mainly decreased readability. You need to read the lines one by one and “save” the contents of Component in your brains memory. When using compose you just quickly glance and notice the compose keyword and understand what is going on.

      You could argue that this is a detail and working with immutable data is more a matter of personal taste. And I can agree to some extent. A longer explanation of immutability and functional programming would be needed to fully grasp this.

      • Actually, I completely agree with you. I think I found the above code somewhere on GitHub just curious because at first glance it seems to be easier to understand.

        Also, if the component is a class then eslint will yell at you as well 🙂