BBV logo

Advanced React Patterns 1 — Let’s talk about Render Props, Function as Child and HOCs

There’s three patterns in React that are meant to be used in case that you want to decouple generic logic, so, this can be shared with others as a library, gist or whatever you want.

Those patterns are Render Props, Function as Child and HOCs. Each of them has its own uses cases plus pros and cons.

I’m going to explain you those patterns and its use cases based on a simple example. We’re going to wrap request animation frame into a component using a different pattern.


Render Props

The idea behind this pattern is that you’ve to build a Stateful Component that wraps logic, the result of the execution of this logic is shared by a callback.

This callback is a prop used by the component you build, and the prop is normally called render.

import React from 'react';
import ReactDOM from 'react-dom';

const styles = {
  fontFamily: 'sans-serif',
  textAlign: 'center',
};

class RequestAnimationFrame extends React.Component {
  state = {
    timeStamp: 0,
  };

  componentDidMount() {
    requestAnimationFrame(timeStamp =>
      this.setState({
        timeStamp,
      })
    );
  }

  render() {
    return this.props.render(this.state.timeStamp);
  }
}

const App = () => (
  <div style={styles}>
    <RequestAnimationFrame render={timeStamp => <h1>{timeStamp}</h1>} />
  </div>
);

ReactDOM.render(<App />, document.getElementById('root'));

Pros

  • It’s fully declarative, you only use React Components and Props. There’s no magic.
  • If the component that use the render props needs to re-render, it only re-renders what’s defined in render function.

Cons

  • Let’s say you need to access the data from componentWillReceiveProps or another lifecycle callback different from render. You can’t, because it’s attached to the render function scope.

Perfect Use Case

  • Use it to define Components wrapping recursive or iterative logic. I found it really useful in those cases. The RequestAnimationFrame component is an ideal example of that.
  • Use it to define Components following the Container/Presentational Component pattern.
  • Use it to define Components that access the React Context API.

Function as Child

It’s pretty similar to Render props pattern, but it differs in the way that it renders.

With Function as Child you use the children prop that it’s present in every component you define to behave like a function, instead of using a callback passed as prop.

It shares all the pros/cons and use cases from render prop. So, the usage of one or another, really depends on what you like the most.

import React from 'react';
import ReactDOM from 'react-dom';

const styles = {
  fontFamily: 'sans-serif',
  textAlign: 'center',
};

class RequestAnimationFrame extends React.Component {
  state = {
    timeStamp: 0,
  };

  componentDidMount() {
    requestAnimationFrame(timeStamp =>
      this.setState({
        timeStamp,
      })
    );
  }

  render() {
    if (typeof this.props.children !== 'function') {
      throw 'Children should be a Function!';
    }

    return this.props.children(this.state.timeStamp);
  }
}

const App = () => (
  <div style={styles}>
    <RequestAnimationFrame>
      {timeStamp => <h1>{timeStamp}</h1>}
    </RequestAnimationFrame>
  </div>
);

ReactDOM.render(<App />, document.getElementById('root'));

HOCs

The HOC pattern is the oldest pattern, it was intended to be a replace of mixins when React transition from having it’s own class model to use ES2015 classes.

The idea behind a HOC is completely similar to the idea of High Order Functions from Functional Programming paradigm.

In simple words, a HOF is a function that returns another function that enhances the ones you passes to it. In the case of HOC, it’s just a function that takes a Component and returns a new one with steroids.

import React from 'react';
import ReactDOM from 'react-dom';

const styles = {
  fontFamily: 'sans-serif',
  textAlign: 'center',
};

const withRequestAnimationFrame = _ => WrappedComponent => {
  class RequestAnimationFrame extends React.Component {
    state = {
      timeStamp: 0,
    };

    componentDidMount() {
      requestAnimationFrame(timeStamp =>
        this.setState({
          timeStamp,
        })
      );
    }

    render() {
      return (
        <WrappedComponent {...this.props} timeStamp={this.state.timeStamp} />
      );
    }
  }

  return RequestAnimationFrame;
};

const App = props => (
  <div style={styles}>
    <h1>{props.timeStamp}</h1>
  </div>
);

const EnhancedApp = withRequestAnimationFrame()(App);

ReactDOM.render(<EnhancedApp />, document.getElementById('root'));

Pros

  • Alternative for Mixins Pattern.
  • Encapsulates logic that can be shared easily.
  • It enables you to access data from any part of the React Component lifecycle.

Cons

  • It adds to you a new component to your virtual DOM tree without knowing. Believe me, a lot of people didn’t notice this.
  • If you encapsulate logic and save results using internal component state, the component you wrap using a HOC will re-render fully. You may not want this, and to avoid this issue you will have to implement SCU.
  • You have to hoist static methods of the wrapped component, if not, you will loose it.
  • You’ve to not forget to pass down props to the wrapped component, if not, you will lose it.

Perfect Use Case

  • Use it when the logic you will try to reuse must be accesible from all the Component Lifecycle of the Wrapped Components.
  • Use it to define Components that do some logic inside React lifecycle callbacks.
  • Use it to define Components that access the React Context API.
arrow back iconPrevious