Building a property passthrough Higher-Order Component for React

Just want the component? Find it at GitHub

Possibly the most frequently re-implemented code across any React component is that used to pass properties through to child components. This stems from the fact that you generally need some sort of input to make the component useful, while you don’t want these component-specific properties polluting the props on your children.

This would be all well and good if it wasn’t for the fact it is so easy to avoid re-implementing this over and over again! I mean, you’re already defining the properties you consume in propTypes – why repeat yourself?

In fact, by passing your React classes through a Higher-Order Component, you can easily add a method which returns all props except the ones specified in propTypes – making writing components that-much-easier (and consistent). I’ll show you how to do it in a moment, but first lets have a look at:

The Old Way Of Doing Things

Say we wanted to write a NavBar component for our app, which is possible to display in two flavours: 'dark' and 'light'. To do this, we’ll accept a theme property and apply different styles to the underlying <div> by assigning a theme-specific className.

To make our NavBar useful in a variety of contexts, we want to make sure that properties such as style and onClick are passed into the generated <div>. But given that we don’t want our theme property to appear in the DOM, we can’t just write <div {...this.props} /> and pass through everything.

How do we accomplish this? Well, the immediately obvious way of doing this is to go through and manually transfer each of the props we want to pass through, rendering something along the lines of this:

const classes = `NavBar NavBar-${this.props.theme} ${this.props.className}`;
return <div style={this.props.style} onClick={this.props.onClick} className={classes}>{this.props.children}</div>;

But this breaks down when we want to use the component in ways we didn’t forsee – like if we wanted to add an onKeyDown handler, for example.

Our component really shouldn’t have to worry about how it’s consumer wants to use the underlying <div>, and as such, it makes sense to pass through all of the received props except those we specifically want to use. To do this, we might follow React’s transferring props documentation and try using ES7’s experimental object rest properties feature:

const { this.props.theme, className, children, ...other } = this.props;
const classes = `NavBar NavBar-${theme} ${className}`;
return <div {...other} className={classes}>{children}</div>;

This isn’t a bad way of doing things – but it could be done better. That’s where propTypes comes in.

Don’t Repeat Yourself

The propTypes class property of your React components is how you tell React about the various properties you expect to receive. For our NavBar class, it might look something like this:

NavBar.propTypes = {
    theme: React.PropTypes.oneOf(['dark', 'light']),
    className: React.PropTypes.string,
};

During development, React uses this to alert you when your components aren’t behaving as expected. But that doesn’t prevent us from using it in other ways!

In particular, we can use it to get a list of properties we don’t want to pass through to our child components:

const omit = Object.keys(NavBar.propTypes)

And then using the except package on npm, we can easily extract these keys from this.props:

const other = except(this.props, omit)

Putting this together, we could write our NavBar component’s render function like so:

const classes = `NavBar NavBar-${this.props.theme} ${this.props.className}`;
const other = except(this.props, Object.keys(NavBar.propTypes))
return <div {...other} className={classes}>{this.props.children}</div>;

Great, we’re not repeating ourself anymore! However, we still have the small problem of the snippet being our most complicated and unwieldy one so far.

Once upon a time, we may have tried to mitigate this by generating our other object from a method in a React mixin – but with ES6 classes, we can go one better.

Higher Order Components

While ES6 classes may look special on the surface, under the hood they’re just sugar for vanilla functions with a bunch of prototype methods. And just like vanilla functions, we can pass them around as arguments and define new properties on their prototype after the fact.

This allows us to modify or wrap our ES6 classes programatically. People call the functions that do this “Higher-Order Components”.

Let’s built a higher order component which adds a passthrough method to the prototype of whatever function we pass in. Actually, maybe let’s get you to write it for practice. It’ll look something like this:

function addPassthroughMethod(component) {
    // Add your `passthrough` method to component here
}

Once you’ve had a shot, check our answer against mine by touching or hovering your mouse over the box below:

import except from 'except'

export default function addPassthroughMethod(component) {
  // TODO: define this as a getter instead of as a fn
  component.prototype.other = function passthrough() {
    const omit = Object.keys(component.propTypes || {})).concat('children')
    return except(this.props, omit)
  }
}


There are some properties, like children, which you never want to automatically pass through – you can add these in your passthrough function so you don’t need to add them to propTypes every time.

Great! Now you can add a passthrough method to any React component just by running addPassthroughMethod on it. Using the new method is as simple as ensuring your propTypes are up to date, and then passing the result of passthrough() into one of the components in your render function:

class MyComponent extends React.Component {
    render() {
        return <div {...this.passthrough()}>{this.props.children}</div>
    }
}

MyComponent.propTypes = {
    ...
}

addPassthroughMethod(MyComponent)

It couldn’t get any simpler. Or could it?

Improving readability with ES7 decorators and class properties

The great thing about Higher-Order Components is they can be used as ES7 class decorators! Combined with ES7’s class properties proposal, you can accomplish the whole thing in an pleasingly simple manner:

@addPassthroughMethod
class Paper extends React.Component {
    static propTypes = {
        ...
    }

    render() {
        return <div {...this.passthrough()}>{this.props.children}</div>
    }
}

See the ES7 decorators and class properties proposals for more details.


Some people may argue against using Decorators/Higher-Order Components to modify the passed in component, and suggest that it would make be more elegant to extend the passed-in class with a render method which passes through the passthrough props to the existing render method as parameters.

While this may look more “functional”, the reality either way is that the decorated class needs to know that it will be decorated. Given that it is easy to compose multiple decorators which modify the prototype, I’d say this is the more pragmatic option.

Theres an NPM module for that

Now you know how to write your own passthrough decorator, and they say knowing is half the battle! But is there any point finishing the battle off when you can just npm install something which does all this (and more)?

npm install react-passthrough

Just like the above example, react-passthrough adds a passthrough() method to your React components. However, unlike our addPassthroughMethod function above, react-passthrough let’s you specify which properties you’d like to always omit (defaulting to ['children']), as well as which properties you’d always like to force inclusion of. Here is an example of usage:

import passthrough from 'react-passthrough'

@passthrough({force: ['disabled', 'tabindex'], omit: ['children', 'form']})
class Control extends React.Component {
    render() {
        return <div {...this.passthrough()}>{this.props.children}</div>
    }
}

react-passthrough was extracted from my Memamug (my open-source React app) – and it isn’t alone! If you’ve found this useful, you’ll may also find some utility from my other components and articles. Sign up for my mailing list to learn about them! In return for your e-mail, you’ll also immediately receive 3 bonus print-optimised PDF cheatsheets – on React, ES6 and JavaScript promises.

I will send you useful articles, cheatsheets and code.

I won't send you useless inbox filler. No spam, ever.
Unsubscribe at any time.

2 Comments Building a property passthrough Higher-Order Component for React

  1. Ali

    Great post. Thank you. Best explanation and usage of es7 decorators I have ever seen. You really nailed it down with simple to complex approach.

    Thank you again.

    Reply
  2. Harry

    Now that PropTypes checkers will be stripped out of React in production (https://facebook.github.io/react/warnings/dont-call-proptypes.html) and people are even very keen on removing PropTypes declarations too. (https://github.com/oliviertassinari/babel-plugin-transform-react-remove-prop-types), is this still safe to use in production?

    It appears to me that the mainstream’s opinion is that PropTypes is just for development. For that reason, in long term, it should be replaced with something like typescript or flow. What are your view of PropTypes as a runtime contract?

    Reply

Leave a Reply

Your email address will not be published.