This is the 4th post of series in which we are going to explore the usage of React with ECMAScript6 and ECMAScript7.

You could find links to all parts of series below:

Code corresponding to this article is available at GitHub.

Update from 18.06.2016: Updated the code and text to use React 15 and Babel 6.

React Logo JavaScript Logo

When using React.createClass() you have a possibility to use so-called mixins. They allow you to mix some additional functionality into your React components. This concept is not unique for React, it also exists in vanilla JS and other languages / frameworks.

It’s no longer possible to use React mixin mechanism for components written in ES6. I won’t dive into the reasons of such a decision (a lot around this topic was written before me). If you are interested, you could check links below for more information around this:

Instead, we are going to concentrate more on concrete examples.

Higher-Order Components instead of Mixins

We will work with CartItem component from part 2 of this series of articles. You could grab its code from here.

Let’s say that along with other controls we would like to show timer with increasing number each second.

To better illustrate the approach, we won’t change the code of CartItem. Instead, we are going to provide some component that will wrap original CartItem and “enhance” CartItem with additional functionality. Such a component is called Higher-Order Component.

This might sound cryptic, but it should become clearer while moving along.

P.S. By “enhancing” I mean simply wrapping component in some function.

Let’s imagine we have IntervalEnhance component:

import React from 'react';
import { IntervalEnhance } from "./intervalEnhance";

class CartItem extends React.Component {
    // component code here
}

export default IntervalEnhance(CartItem);

Now it’s time to write this intervalEnhance component. We’ll add new file called intervalEnhance.jsx:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import React from 'react';

export var IntervalEnhance = ComposedComponent => class extends React.Component {

    static displayName = 'ComponentEnhancedWithIntervalHOC';

    constructor(props) {
        super(props);
        this.state = {
            seconds: 0
        };
    }

    componentDidMount() {
        this.interval = setInterval(this.tick.bind(this), 1000);
    }

    componentWillUnmount() {
        clearInterval(this.interval);
    }

    tick() {
        this.setState({
            seconds: this.state.seconds + 1000
        });
    }

    render() {
        return <ComposedComponent {...this.props} {...this.state} />;
    }
};

Let’s take a closer look.

  • Line 3. Part ComposedComponent => class extends React.Component - this is the same as defining the function that returns class. ComposedComponent is component we want to “enhance” (in the code above it is CartItem). By using export var IntervalEnhance we are able to export the whole function as IntervalEnhance (refer to CartItem code above).
  • Line 5. Helpful for debugging. The component will be named as ComponentEnhancedWithIntervalHOC in React DevTools.
  • Lines 7-12. We initialize the state of a component with seconds equal to zero.
  • Lines 14-26. Providing lifecycle hooks that will start and stop interval for this component.
  • Line 29. Most interesting thing here. This line will bring all state / props of our “enhancer” component and transfer to CartItem component. By doing this CartItem will get access to this.state.seconds property.

Last our step is to change render method of CartItem component. We’ll output this.state.seconds directly to the view:

import React from 'react';
import { IntervalEnhance } from "./intervalEnhance";

class CartItem extends React.Component {
    // component code here
    
    render() {
        return <article className="row large-4">
                <!-- some other tags here -->                    
                <p className="large-12 column">
                    <strong>Time elapsed for interval: </strong>
                    {this.props.seconds} ms
                </p>    
            </article>;
        }
}
export default IntervalEnhance(CartItem);

Open the page in a browser and you will see label ticking each second:

Screenshot

Notice - all this is done without touching CartItem component (apart from render method)! This is why Higher-Order Components are so powerful.

Now, please take a look at the final code. If something is unclear I am glad to see your feedback in comments.

Use ES7 Decorators instead of Mixins

If you like ES7 Decorators more then it’s possible to use them in a similar manner.

First, install babel-plugin-transform-decorators-legacy:

npm install --save-dev babel-plugin-transform-decorators-legacy

And grab modified gulpfile.js from the GitHub repository.

Then:

import React from 'react';
import { IntervalEnhance } from "./intervalEnhance";

@IntervalEnhance
export default class CartItem extends React.Component {
    // component code here
}

What about PureRenderMixin?

If you use mixins like PureRenderMixin then there are different approaches bringing such a functionality to React components written in ES6. One of them is following (all credits go to @aputinski at this gist):

class Foo extends React.Component {
   constructor(props) {
     super(props);
     this.shouldComponentUpdate = React.addons.PureRenderMixin.shouldComponentUpdate.bind(this);
   }
   render () {
     return <div>Helllo</div>
   }
 }

Conclusion

Higher-Order Components are powerful and expressive. Currently, they are widely used and are a replacement for old mixin syntax. Feel free to create your own mechanism for dealing with mixing functionality between components.

One of the examples of its usage is Relay framework. Relay is complete React-based framework released by Facebook. Each component your write could be wrapped into Relay Container for automatic retrieval of data dependencies (and many other things). And this is truly convenient and seems like a magic from the first look.

Next two articles would be about a bit different topic - integrating of our project with JSPM or Webpack.

This should bring not only ES6 support but a bunch of other helpful utilities. Stay tuned!

Further Reading