Migrating React.js Components From ES5 To ES6

 ·  · 

A comparison of writing React.js components in ES5 syntax and ES6 syntax.

 

1 Export and import

1.1 ES5

Export:

/* bookitem.js */

var React = require('react');

var BookItem = React.createClass({
    // ...
});

module.exports = BookItem;

Import:

var BookItem = require('./bookitem.js);

1.2 ES6

Export:

/* bookitem.js */

import React from 'react';

class BookItem extends React.Component {
    // ...
}

export default BookItem;

Or

/* BookItem.js */

import React from 'react';

export default class BookItem extends React.Component {
    // ...
}

Import:

import BookItem from './bookitem.js';

 

2 Create components

2.1 ES5

var React = require('react');

var BookItem = React.createClass({
    // ...

    render: function() {
        return (
            <div><h3>{this.props.bookname}</h3></div>
        );
    }
});

2.2 ES6

import React from 'react';

class BookItem extends React.Component {
    // ...

    render() {
        return (
            <div><h3>{this.props.bookname}</h3></div>
        );
    }
}

 

3 Define methods in component

The function keyword is not needed in ES6, nor is the comma separator between method declarations.

3.1 ES5

var BookItem = React.createClass({
    // ...

    handleClick: function() {},
    render: function() {
        return (
            <div><h3>{this.props.bookname}</h3></div>
        );
    }
});

3.2 ES6

class BookItem extends React.Component {
    // ...

    handleClick() {}
    render() {
        return (
            <div><h3>{this.props.bookname}</h3></div>
        );
    }
}

 

4 Initialize state

4.1 ES5

var BookItem = React.createClass({
    getInitialState: function() {
        return {
            isAddedToCart: false
        }
    }

    // ...
});

4.2 ES6

class BookItem extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            isAddedToCart: false
        }
    }

    // ...
}

 

5 Default props

5.1 ES5

var BookItem = React.createClass({
    getDefaultProps: function() {
        return {
            cover: './images/default_cover.png'
        }
    }

    // ...
});

5.2 ES6

class BookItem extends React.Component {
    // ....
}

BookItem.defaultProps = {
    cover: './images/default_cover.png'
};

5.3 ES7

class BookItem extends React.Component {
    static defaultProps = {
        cover: './images/default_cover.png'
    }

    // ....
}

 

6 PropTypes

6.1 ES5

var BookItem = React.createClass({
    getDefaultProps: function() {
        return {
            cover: './images/default_cover.png'
        }
    },
    propTypes: {
        cover: React.PropTypes.string.isRequired
    },

    // ...
});

6.2 ES6

class BookItem extends React.Component {
    // ....
}

BookItem.propTypes = {
    cover: React.PropTypes.string.isRequired
}

6.3 ES7

class BookItem extends React.Component {
    static propTypes = {
        cover: React.PropTypes.string.isRequired
    }

    // ....
}

 

7 this reference inside component methods

7.1 ES5

var BookItem = React.createClass({
    // ...

    getInitialState: function() {
        return {
            isAddedToCart: false
        }
    },
    handleClick: function(e) {
        alert(this.state.isAddedToCart);

        // 'this' here has already been bound to the current component instance
        this.setState({ isAddedToCart: !this.state.isAddedToCart });
    },
    render: function() {
        return (
            <div>
                <h3>{this.props.bookname}</h3>
                <input type="checkbox" defaultChecked={false} onChange={this.handleClick} /> Buy
            </div>
        );
    }
});

React.createClass will automatically bind all its method to the instance of component object in ES5.

7.2 ES6

However, we have to bind this context manually in React.Component, otherwise the value of this inside component methods will be null.

7.2.1 Bind explicitly
class BookItem extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            isAddedToCart: false
        }
    }
    handleClick(e) {
        alert(this.state.isAddedToCart);

        // We need bind 'this' manually.
        // Otherwise 'this' will be null.
        this.setState({ isAddedToCart: !this.state.isAddedToCart });
    }
    render() {
        return (
            <div>
                <h3>{this.props.bookname}</h3>
                <input type="checkbox" defaultChecked={false} onChange={this.handleClick.bind(this)}  /> Buy
            </div>
        );
    }
}

Alternatively we can bind the context of this inside the constructor().

class BookItem extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            isAddedToCart: false
        }

        // bind
        this.handleClick = this.handleClick.bind(this);
    }
    handleClick(e) {
        alert(this.state.isAddedToCart);

        // We need bind 'this' manually.
        // Otherwise its value will be null.
        this.setState({ isAddedToCart: !this.state.isAddedToCart });
    }
    render() {
        return (
            <div>
                <h3>{this.props.bookname}</h3>
                <input type="checkbox" defaultChecked={false} onChange={this.handleClick}  /> Buy
            </div>
        );
    }
}
7.2.2 Use ES6 fat arrow function

Furthermore, we can use the fat arrow function syntax: => in ES6. Because ES6 fat arrow functions will preserve the context of this when they are called.

class BookItem extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            isAddedToCart: false
        }
    }
    handleClick(e) {
        alert(this.state.isAddedToCart);

        // We need bind 'this' manually.
        // Otherwise its value will be null.
        this.setState({ isAddedToCart: !this.state.isAddedToCart });
    }
    render() {
        return (
            <div>
                <h3>{this.props.bookname}</h3>
                <input type="checkbox" defaultChecked={false} onChange={ e => this.handleClick(e) } /> Buy
            </div>
        );
    }
}

Or redefine the event handling method using the fat arrow function syntax in the constructor().

class BookItem extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            isAddedToCart: false
        }

        this._handleClick = (e) => this.handleClick(e);
    }
    handleClick() {
        alert(this.state.isAddedToCart);

        // We need bind 'this' manually.
        // Otherwise its value will be null.
        this.setState({ isAddedToCart: !this.state.isAddedToCart });
    }
    render() {
        return (
            <div>
                <h3>{this.props.bookname}</h3>
                <input type="checkbox" defaultChecked={false} onChange={this._handleClick} /> Buy
            </div>
        );
    }
}

Or just directly define the response method using fat arrow function syntax inside the constructor().

class BookItem extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            isAddedToCart: false
        }

        this.handleClick = (e) => {
            alert(this.state.isAddedToCart);
            this.setState({ isAddedToCart: !this.state.isAddedToCart });           
        }
    }
    render() {
        return (
            <div>
                <h3>{this.props.bookname}</h3>
                <input type="checkbox" defaultChecked={false} onChange={this.handleClick} /> Buy
            </div>
        );
    }
}

8 Summary

In this article, we gave a cheat sheet for React.js with ES6 syntax.