Enhancing React Components with decorators

Decorators are a feature that didn’t make it in ES7, but are currently a stage 2 proposal so it’s fair to assume they’re going to be a part of the next standard. Even though they are an experimental feature, decorators have been widely adopted in most modern frameworks. As an ex-Python developer this made me very excited and I got straight into looking for ways to incorporate this new feature into my code.

So, let’s jump right into it by implementing something useful!

Changing the tab title when changing the route

Medium makes use of this feature a lot

Most popular websites use the tab title to compliment the page content. In the past implementing this with server-side rendered applications was fairly straightforward, now not so much.

The first thing you probably thought about is putting it right into the router configuration. In React it would probably look something like this:

const setTitle = (title) => () => document.title = title;
<Route
path="/profile"
component={Profile}
onEnter={setTitle('Profile')}
/>

This is a pretty clever and clean approach, maybe that’s all you needed in your application. But with large application things can get a little bit complicated.

What if you have nested views and you want to change the title independently of the route? Or what if your title must contain some async data?

I know! I’ll just set the title in my component!

You say as you realize a couple of minutes later that it evolves into a separation of concerns nightmare, spreading out title updating logic across componentDidMount, componentWillReceiveProps and other life cycle events.

So, how can we handle this?

Creating a title changing decorator

First, let’s see how it will look.

@setTitle('Profile')
class Profile extends React.Component {
....
}

Now we can get into implementing it!

A decorator is a callable that takes a function as an argument and returns a replacement function. — Python wiki
/*
title
is a string that will be set as a document title
WrappedComponent is what our decorator will receive when
put directly above a component class as seen in the example above
*/
const setTitle = (title) => (WrappedComponent) => {
return class extends React.Component {
componentDidMount() {
document.title = title
}
      render() {
return <WrappedComponent {...this.props} />
}
}
}

That pretty much sums it. As soon as our wrapper component mounts, the title we provided will be set.

But wait! What about asynchronous data?

Modifying our decorator so that it will work asynchronously

A simple approach would be changing setTitle’s parameter to a callback function. Using it will now look like this:

@setTitle((props) => {
if(!props.user) return 'Loading profile...'
return `${prop.user.name}'s Profile`
}
class Profile extends React.Component {
....
}

And now, modifying our decorator:

const setTitle = (getTitle) => (WrappedComponent) => {
return class extends React.Component {
updateTitle = (props) => {
// Check if the callback has returned something,
// and if so - update the title
const title = getTitle(props)
if(title) {
document.title = title
}
}
      componentDidMount() {
this.updateTitle(this.props)
}
      componentWillReceiveProps(props) {
this.updateTitle(props)
}
      render() {
return <WrappedComponent {...this.props} />
}
}
}

Great! Now our decorator will make continuous checks every time it receives props and update the title accordingly. You can see the final result below:

✨ ✨ ✨

Thanks for reading! This is just an experiment of mine, you can check out the source at https://github.com/gigobyte/react-document-title-decorator

If you want to see some other (more mature) solutions, check out:

If you want to read more about decorators you can read this amazing article by Addy Osmani.