React, JSX, and CoffeeScript

February 11, 2014

React is neat. It's a reimagining of how your DOM relates to your app state that dramatically simplifies code without being an enormous framework*. It also breaks a rule that I've never cared for, where your templates are supposed to be separate from your code.

In React's world, each component (re-)renders its entire virtual DOM on any state change; React then manages updating the on-page DOM based on what changed. This means the rendering code only needs to model the static state of a component, rather than poking around in the DOM to transition between states.

To facilitate this rendering, React includes an optional library/tool called JSX that lets you inline HTML into your render function. The translation is relatively simple. This code:

render: function() {
  return <p><a href="foo">bar</a></p>;
}

becomes, post-translation, the not-keyboard-friendly but not hard to understand:

render: function() {
  return React.DOM.p(null, React.DOM.a({href:"foo"}, 'bar'));
}

In words, the tag becomes a function call, the attributes become an object passed as the first argument, and the contents of the tag are further parameters to the function.

The nice thing about interleaving code and DOM is that you can use all the normal JavaScript functionality in your template. Here's another example from the React docs with an embedded Array.map:

return (
  <ol>
    {this.results.map(function(result) {
      return <li key={result.id}>{result.text}</li>;
    })}
  </ol>
);

CoffeeScript instead of JSX

If you're using CoffeeScript, your source code isn't JavaScript to begin with. But turns out that CoffeeScript's flexible syntax makes it relatively painless to use the underlying API directly.

Start with shortening the DOM alias and writing more or less the same code as above. Also note that you don't need to explicitly return as the last statement in a function is implictly returned, and that the function literal syntax for argumentless function is just a bare ->:

R = React.DOM
# ...

render: ->
  R.p(null, R.a({href:'foo'}, 'bar'))

But you can do better. First, CoffeeScript knows to insert the curlies on an object literal because of the embedded colon.

  R.p(null, R.a(href:'foo', 'bar'))

And then you can remove the parens by splitting across lines. When providing args to a function, a comma+newline+indent continues the argument list. Much like Python, the visual layout follows the semantic nesting.

  R.p null,
    R.a href:'foo', 'bar'

In fact, beyond the first argument, the trailing commas are optional when you have newlines. Here's the same thing again with two links inside the <p>:

  R.p null,
    R.a href:'foo', 'bar'  # note omitted comma here
    R.a href:'foo2', rel:'nofollow', 'second link'

CoffeeScript also makes every statement into an expression, which is a familiar feeling coming from functional programming. It means you can use statement-like keywords like if and for on the right hand side of an equals sign, or even within a block of code like the above.

Here's a translation of the (7-line) <ol> example from above.

R.ol null,
  for result in @results
    R.li key:result.id, result.text

There is one final feature of CoffeScript that I find myself using, which is an alternative syntax for object literals. For example, suppose in the above example the "key" attribute needs to be computed from some more complicated expression:

R.ol null,
  for result, index in @results
    resultKey = doSomeLookup(result, index)
    R.li key:resultKey, result.text

The simplification is that, within a curly-braced object literal, entries without a colon use the variable name as the key. The above could be equivalently written:

R.ol null,
  for result, index in @results
    key = doSomeLookup(result, index)
    R.li {key}, result.text

This is particularly useful when the attributes you want to set have meaningful names — key is pretty vague, but if you construct an href and a className variable it's pretty clear where they are going to be used. These can be mixed with normal key-value pairs, too, like:

href = ...
className = ...
R.li {href, className, rel:'nofollow'}, ...

Putting it all together, here's a larger example, part of an implementation of an "inline edit" widget. To the user, this widget is some text with a "change" button to its right, where clicking on "change" swaps the line of text out for an edit field positioned in the same place, allowing the user to make a change to the value directly. (Like how it works in a spreadsheet.) The first branch of the if is the widget's initial state; the @edit function flips on the @state.editing flag.

render: ->
  if not @state.editing
    R.div null,
      @props.text
      ' '  # space between text and button
      R.span className:'link-button mini-button', onClick:@edit, 'change'
  else
    R.div style:{position:'relative'},
      R.input
        style:{position:'absolute', top:-16, left:-7}
        type:'text', ref:'text', defaultValue:@props.text
        onKeyUp:@onKey, onBlur:@finishEdit

To get a feel for these rules, you can just experiment and look at the generated JavaScript. Or you can go to coffeescript.org and click the "Try CoffeeScript" tab, where you can enter nonsense expressions there just to experiment with the syntax.

* Though I do wish it was smaller. I wish I could cut all the support for old IE bits and the event handling abstractions.