Given enough coffee I could rule the world



React and Coffeescript (without JSX)

ReactJS is taking the web by storm. Annoyingly it comes with another Javascript preprocessor - JSX. If you are already in a coffeescript environment, then you can give React a try without needing to learn JSX or adding it to your toolchain.

Coffeescript

Coffeescript is a Javascript superscript (it transpiles to Javascript) similar to TypeScript and ActiveScript. It was written in 09/10 to show off "the good parts" of Javascript. It ships as the default Javascript engine with Ruby on Rails since version 3.1 which has helped make it one of the most popular JavaScript superscript on Github to date.

Underneath that awkward Java-esque patina, JavaScript has always had a gorgeous heart. CoffeeScript is an attempt to expose the good parts of JavaScript in a simple way. -- coffeescript.org

ReactJS

React is the front end view library (note: it's not a framework) by Facebook which is heralded by developers for making complex UIs easy to manage, while (often) improving performance of web applications. React's core concept is stateful views. Given the same state it will always render the same view, regardless of how it arrived at this state. It was built to erradicate awkward to reproduce errors from complex UIs, as detailed in the presentation introducing ReactJS in 2013 by Tom Occhino and Jordan Walke.

It comes with it's own language, JSX, which allows the use of HTML-esque XML markup to create view components inside Javascript files. Think of your typical templating language which embeds code inside a HTML view, only in reverse.

But honestly, I'm not a fan of it. As a Coffeescript dev, the idea of all those extra angle brackets from the JSX syntax inside the render method is unappealing. A side effect of having your view inside your code, is what happens when you want code inside your view (eg. for attaching click listeners or binding values). The result is deeply nested : javascript expressions inside curly braces inside HTML like markup inside your JS file. Because you are limited to JS expressions inside the view ( eg. text within a span, conditional classes) means that you have to resort to tertiary operators or anonymous, self-executing functions rather than the more legible if / else statements.

Take a look at a basic form component straight from the React docs


  class NameForm extends React.Component {
    constructor(props) {
      super(props);
      this.state = {value: ''};

      this.handleChange = this.handleChange.bind(this);
      this.handleSubmit = this.handleSubmit.bind(this);
    }

    handleChange(event) {
      this.setState({value: event.target.value});
    }

    handleSubmit(event) {
      alert('A name was submitted: ' + this.state.value);
      event.preventDefault();
    }

    render() {
      return (
        <form onSubmit={this.handleSubmit}>
          <label>
            Name:
            <input type="text" value={this.state.value} onChange={this.handleChange} />
          </label>
          <input type="submit" value="Submit" />
        </form>
      );
    }
  }

The React component representing a form with an input element is represented as an ES6 Class. When constructed, the component is assigned a base state where the input value will be saved. This state is used within the render method, which is the equivalent of a one-way data binding in frameworks like AngularJS. To make the view update when the user updates the value in the text input field, an onChange handler needs to be attached, which will update the state via the this.setState method. This triggers a re-render of the component with the updated value.

This value can be pulled from the state to be used in the onSubmit handler, where you could post it to a server or persist in localstorage. But one caveat is the need for binding the component instance to the event listeners, as otherwise the event would override this within the scope of the callback methods.

It's all fairly straightforward, but it's also a trivial example. It can be made even cleaner by dropping the JSX syntax and rewriting the whole thing in Coffeescript

Converting to Coffeescript

Replacing JSX

It is of course possible to write React without JSX in vanilla Javascript. But without JSX, we need to manually call of the React.DOM factories to build components out of the React representations of DOM elements. Markup as simple as the form above becomes convoluted when we have to call the methods and pass it object properties and child components.

Here is the render method in vanilla Javascript:


  render() {
    return React.DOM.form({ onSubmit: this.handleSubmit },
      React.DOM.label({}, "Name", 
        React.DOM.input({ type: 'text', value: this.state.value, onChange: this.handleChange })
      )
      React.DOM.input({ type: 'submit', value: 'Submit' })
    )
  }  

It's not pretty, especially due to the deeply nested DOM components (React.DOM.x). But there are a lot of Coffeescript features which come in useful with React.

Destructuring Assignment

The destructuring assignment allows us to pull object properties into separate variables with ease. Using this you can pull only those DOM elements which you need for each component into the parent scope:


  { form, label, input } = React.DOM

@ === this

A useful shortcut in coffeescript is that the @ symbol refers to this. This is a convention borrowed from ruby where @ denotes any instance variables. If you want to access a property on the this, then you can even skip the dot :


  form
    onSubmit: @handleSubmit

Here you can already see the form factory which was pulled from React.DOM being called straight from the root scope within our file.

Indentation instead of brackets

This is the one feature of Coffeescript which got me hooked. Once you remove all the excessive brackets, semicolons and commas from Javascript, it becomes an elegant language. No brackets are used to call a function (given that it is being passed arguments). No brackets are needed to wrap a function body or code block. No brackets are needed to denote an object and commas are optional. Here is the resulting render function in Coffeescript


  render: ->
    form
      onSubmit: @handleSubmit
      label null,
        'name'
        input
          type: 'text'
          value: @state.value
          onChange: @handleChange
      input
        type: 'submit'
        'Submit'

Isn't it beautiful?

There are two Coffeescript features involved here. If you call a method in Coffeescript, and the first param is an object, then you can just pass it starting on the next line. Eg, the setState call from handleChange :


  @setState
    value: event.target.value

If the object argument has more props, just continue adding them on new lines.

Secondly, the remaining children which are being passed to the method after the initial properties object are distinguished from the object simply by keeping the same indentation as the object, but not having keys. Each child component can the be passed on it's own line, and via indentation, you can place components inside the same parent even if they have complex children - eg. the submit button and label are both direct children of the form.

The closing tags from JSX, which became closing parenthesis in Javascript can be dropped as they simply aren't required.

I've also left out the return statement, as Coffeescript implicitly returns the last statement in every block. But you will notice one caveat in the above example, which is that we have to pass null to label as it has no properties, but does have a child (the text 'name'). Ideally I would like to see React components to accept only a child element (or array) to clean out this one inconsistency.

Binding context

When dealing with DOM event listeners, we need to remember that this inside a callback refers to the element which the event was caught on. But in React we usually want it to be the component, as we can access the DOM element via the event argument. This is why the handleChange and handleSubmit methods have this binded to them inside the class:


  class NameForm extends React.Component {
    constructor(props) {
      super(props);
      this.state = {value: ''};

      this.handleChange = this.handleChange.bind(this);
      this.handleSubmit = this.handleSubmit.bind(this);
    }

In Coffeescript the "fat arrow" => automatically binds the context in which the function is defined (eg. the class) to the function's body.

String interpolation

You'll have noticed that while the Javascript used double quotes for its strings, I've been using single quotes in the Coffeescript version. That is because in Coffeescript, double quotes are used for string interpolation using the #{} syntax. For example, if you wanted to place a name inside a welcome message, then the Coffeescript would be:


  console.log "Hello #{name} and welcome to Caffeinated.tech!"

Putting it all together

Finally, we get a complete React Component in Coffeescript:


  class NameForm extends React.Component
    constructor(props) ->
      super(props)
      @state = 
        value: ''

    handleChange: (event) =>
      @setState
        value: event.target.value

    handleSubmit: (event) =>
      alert "A name was submmited: #{@state.value}"
      event.preventDefault()

    render: ->
      form
        onSubmit: @handleSubmit
        label null,
          'name'
          input
            type: 'text'
            value: @state.value
            onChange: @handleChange
        input
          type: 'submit'
          'Submit'

Summary

Coffeescript is a language true to its roots - exposing the good parts of Javascript. This approach still works with newer libraries with highly specialized libraries like ReactJS. If you want to delve into the ReactJS world, but don't fancy adding another pre-compile step to your coffeescript project, then don't hold back.

While resources for the specific combination of Coffeescript and ReactJS are few and far between, there are some I can recommend. Arkency run a great development blog with many posts on using ReactJS with a Ruby on Rails / Coffeescript project.


If you have feedback, or would like some more posts on the topic of ReactJS and Coffeescript then let me know.