Porting a Rails frontend from CoffeeScript to ES6 and JSX - examples

We are working on a new version of our bestseller book - Rails meets React.js. Currently, the book is based in CoffeeScript and we want to port it to ES6. The update will be free for everyone, who bought the CoffeeScript version

Here are some examples showing a process of porting from CoffeeScript to ES6 and JSX.

JSX

In our previous version of this book, we didn’t use JSX because it does not fit well with CoffeScript. With ES6 it is different, JSX fits here very well.

Before:

  Stats = React.createClass
    contextTypes:
      user: React.PropTypes.object
    render: ->
      React.DOM.div null,
        "Won: #{@context.user.won}"
        React.DOM.br null
        "Lost: #{@context.user.lost}"
        React.DOM.br null
        React.DOM.a
          href: "http://www.example.org/stats/#{@context.user.id}",
          "(see the whole stats of #{@context.user.name})"

  stats = React.createFactory(Stats)

After:

class Stats extends React.Component {
  render() {
    return (
      <div>
        Won {this.context.user.won}
        <br />
        Lost {this.context.user.lost}
        <br />
        <a href={`http://www.example.org/stats/${this.context.user.id}`} >
          (see the whole stats of {this.context.user.name})
        </a>
      </div>
    );
  }
}

Note ES6 template literal `http://www.example.org/stats/${this.context.user.id}` which we are using to construct string url.

ES6 classes

Instead of using React.createClass we are now using ES6 class syntax:

Before:

OneTimeClickLink = React.createClass
  render: ->
    React.DOM.div(
      {id: "one-time-click-link"},
      React.DOM.a(
        {href:"javascript:void(0)"},
        "Click me"
      )
    )

After:

class OneTimeClickLink extends React.Component {
  render() {
    return (<div id="one-time-click-link">
      <a href="javascript:void(0)">
        Click me
      </a>
    </div>);
  }

State initialization

Since getInitialState does not work with classes syntax, we are initializing state in a constructor. Like in this example:

Before:

DateWithLabel = React.createClass
  getInitialState: ->
    date: new Date()
  render: ->
    DOM.div

After:

class DateWithLabel extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      date: new Date()
    };
  }

  render() {
    return (<div></div>);
  }
}

Bind instance methods

In class syntax, React doesn’t bind all methods automatically. So, we are binding them in our constructor.

Before:

  OnOffCheckbox = React.createClass
    getInitialState: ->
      toggled: false

    toggle: ->
      @setState toggled: !@state.toggled

    render: ->
      React.DOM.input
        key: 'checkbox'
        type: 'checkbox'
        id: @props.id
        checked: @state.toggled
        onChange: @toggle

After:

  class OnOffCheckbox extends React.Component {
    constructor(props) {
      super(props);
      this.state = {
        toggled: false
      };
      this.toggle = this.toggle.bind(this);
    }

    toggle() {
      this.setState({ toggled: !this.state.toggled });
    }

    render() {
      return (
        <input
          key="checkbox"
          type="checkbox"
          id={this.props.id}
          checked={this.state.toggled}
          onChange={this.toggle}
        />
      );
    }
  }

Default props and validations

defaultProps , propTypes and contextTypes must be defined outside the class body. Here are some examples:

Before:

  OnOffCheckbox = React.createClass
    getDefaultProps: ->
      initiallyToggled: false

    getInitialState: ->
      toggled: @props.initiallyToggled

    toggle: ->
      @setState toggled: !@state.toggled

    render: ->
      React.DOM.input
        key: 'checkbox'
        type: 'checkbox'
        id: @props.id
        checked: @state.toggled
        onChange: @toggle

After:

  class OnOffCheckbox extends React.Component {
    constructor(props) {
      super(props);
      this.state = {
        toggled: props.initiallyToggled
      };
      this.toggle = this.toggle.bind(this);
    }

    toggle() {
      this.setState({ toggled: !this.state.toggled });
    }

    render() {
      return (
        <input
          key="checkbox"
          type="checkbox"
          id={this.props.id}
          checked={this.state.toggled}
          onChange={this.toggle}
        />
      );
    }
  }
  OnOffCheckboxWithLabel.defaultProps = {
    initiallyToggled: false
  };

The same goes for propTypes:

Before:

Blogpost = React.createClass
  propTypes:
    name: React.PropTypes.string.isRequired

  # ...

After:

class Blogpost extends React.Component {
  // ...
}
Blogpost.propTypes = {
  name: React.PropTypes.string.isRequired
};

Summary

Code written in ES6 with JSX can be pretty clean. I’ve used ESLint which helped me to keep syntax clean and free of errors. Here you can find a Blogpost how correctly configure it for your editor.

If you are interested in learning how to use React with Rails, with the ES6 syntax, we are working on a new version of Rails meets React.js. It will be free for everyone who bought previous CoffeeScript version.