
React, JSX, and CoffeeScript - bslatkin
http://neugierig.org/software/blog/2014/02/react-jsx-coffeescript.html
======
sandover
Like the post author, I use Coffeescript+React -- and don't care for JSX. (I'd
go further and say that XML of any kind has shown itself unfit for direct
editing by humans, but that's a larger discussion.)

Coffeescript has its problems as well, though, and one of the glaring issues
is cutesy syntactic corner cases. Some of these are ok, taken individually,
when they make the code maybe slightly more terse -- or more clear. But in the
aggregate, they can leave us with something that is baroque, off-putting to
the uninitiated, and hard to maintain. That's pretty much what I see in code
here.

Having the curly braces around an object literal be optional was a language
mistake: it simply introduces ambiguity. It doesn't save many keystrokes, and
it wastes the time of the people who may eventually have to read code like
this

    
    
        R.p null,
          R.a href:'foo', 'bar'
          R.a href:'foo2', rel:'nofollow', 'second link'
    

Quick, is rel:'nofollow' part of the same object as href:'foo2', or is is the
second argument to R.a?

More alarm bells went off when I learned that we are using this Coffeescript
clunker I did not know about previously: "beyond the first argument, the
trailing commas are optional when you have newlines".

Wat? _Just the first line_ needs a comma? Did Ruby teach us nothing, folks?
Irregular, tricksy syntax like that is a caterpillar, waiting to turn into a
beautiful butterfly of a bug when some stranger innocently barges in to make a
change 6 months from now.

Even if the language offers features like that, don't use them.

~~~
jashkenas
The language offers features so that you can use them well. Any bit of syntax
can be abused. `))))))`, for example ;)

Optional braces around object literals allow you to write lines like this:

    
    
        rectangle x: 10, y: 15, width: 500, height: 500
    

... and having newlines separate elements in lists makes a _ton_ of sense in a
language where whitespace is significant:

    
    
        requires = [
          "jquery"
          "underscore"
          "d3"
        ]
    

... and _also_ helps with the famous trailing-comma-in-IE error and comma-
first controversy.

If you're writing something that's convoluted enough that the features aren't
helping you ... don't use 'em.

~~~
sandover
Hi Jeremy, if you'll please pardon the the somewhat flame-y tone above -- and
the Coffeescript language is 'done' so these subjects are mostly moot anyway
-- my experience has suggested that:

\- optional braces cause more ambiguity (manifesting as either strange parse
error messages on the front side, or bugs on the back side) than is warranted
by the need to write:

    
    
        rectangle { x: 10, y: 15, width: 500, height: 500 }
    

\- newlines separating elements of a list is good, but it's really confusing
to me why the following program would/should work with no comma after the "a":

    
    
        foo = (a, b, c) -> console.log a, b, c
        foo "b",
          "a"
          "r"
    

Plainly, it _does_ work, but to me it feels like using a hole in my sweater to
scratch an itch on my elbow.

~~~
duckduckblert
I don't know coffeescript, but I'd guess it's because the first comma
indicates "this is an argument list," so the next few items get treated with
list/array syntax rules where commas aren't required.

(Had this comment thread left open in my browser since Friday. Noticed no one
replied so I figured "why not," even though I might be wrong :V)

------
tel
Another trick I like is to import the React functions you want all at once

    
    
        {div, ol, li} = React.DOM # global exports
        div null,
          ol null,
            for result, index in @results
              {a, span} = React.DOM # local exports
              key = doSomeLookup( result, index )
              href = otherLookup( key )
              li {key}, [span(null, "Hello "), a({href}, "world")]

~~~
pselbert
That changes the "feel" of composing without JSX more than anything I've seen
so far. Trying to work JSX into my compiling pipeline was an uncomfortable
hassle. I'm going to give this a try.

~~~
lowboy
If you're looking for a more flexible compilation/build pipeline, I'd suggest
gulp[0]. I have a project that uses gulp-react (among other things) and it's
blazing fast. Here's my gulpfile[1].

[0]: [http://gulpjs.com](http://gulpjs.com)

[1]:
[https://github.com/jjt/LUXTRUBUK/blob/master/gulpfile.js](https://github.com/jjt/LUXTRUBUK/blob/master/gulpfile.js)

------
arasmussen
I'm torn. Syntax-wise, I find that many programming languages are similar
enough that moving from one to another is pretty straightforward. Even
Objective-C makes some kind of sense to a Java/PHP/Python/etc programmer.

But CoffeeScript doesn't feel this way to me, which makes me want to stay far
away. Yet so many smart people are building cool things with it, which makes
me wonder if I just need to suck it up and get familiar with this new syntax.

~~~
elwell
If you learned Objective-C, CoffeeScript is a walk in the park. A peaceful,
elegant, enjoyable walk in the park.

~~~
erokar
Very true.

------
chenglou
React contributor here. The bit about removing event handling abstraction
doesn't make much sense.

1\. Since React's event handling is also virtual (just like its DOM), cross-
browser inconsistencies are taken care of for you. Sure, you could always
attach your native events after mounting a component, but unless you're using
jQuery or something with very good cross-browser support, you're bound to trip
over the same browser bugs we already solved (IE8+!).

2\. The virtual event system is super performant. It does event delegation for
free. Internally, it's implemented as one single event handler at the top; no
need to worry about whether you should put a click event on the list items, or
on the list itself anymore. Events are also pooled.

3\. Since the system's virtual, it opens doors to custom event plugins, i.e.:

    
    
      <div onTapHold={bla} />
    

In fact, React's mobile tap event is a custom plugin. Removes the 400ms delay.
You get the point.

~~~
evmar
(I'm the post author.) Thanks for reading and commenting!

As I mentioned in the footnote, for my purposes (an app where I am likely to
be the only user) I don't care about IE8 support, so it's kind of a bummer
that I have to ship support for it. (In my day job I use Google's Closure
library which has the same problem -- lots of additional complexity to work
around IE bugs. I appreciate that removing this support isn't as simple as
just compiling it out, too.) Similarly for the 300ms delay: the delay is gone
on current Chrome on mobile (when you're using width=device-width; I only know
this because I used to work on Chrome), which is the mobile browser I use.

Another way of saying this is that React is a combination of the virtual DOM
stuff and a bunch of other "useful webapp stuff" like making events work
across browsers and installing event listeners on the root element etc. I
appreciate for you and the React authors' purposes those are the same problem
and difficult to disentangle, but all I wanted to use was the virtual DOM
part. (I guess the analogue would be like thinking Rails's ActionDispatch
library is neat but then discovering you had to use their code-generation
scripts and ORM library as well. Sorry if that metaphor failed, I'm not so
knowledgeable about Rails.)

Anyway, since I'm mostly just tinkering with React etc. the size thing doesn't
matter so much, which is why I put the comment way down in a footnote. But the
whole "you must swallow the whole thing at once" thing is also the reason I've
never looked into Ember or whichever other comparable framework. Maybe at some
point, as a continued learning exercise, I'll attempt to reimplement the
subset of React that I actually use to better understand the design space you
already know.

~~~
vjeux
Removing event management from React core is a common request but it wouldn't
really be a wise decision.

1\. Need a way to manage events: All the applications are handling events in
one way or another. So you've got to have a way to implement event handling in
a React application.

2\. Cannot use existing event libraries: Because React manages its own Virtual
DOM and has a very declarative nature, it is not easy to use raw DOM
imperative APIs in other to implement event handling. Therefore, you cannot
just plug in any event handling library, you've got to have one that works on-
top of the Virtual DOM abstraction.

3\. Performance is a requirement: The way you code in React is by describing
the way you want the DOM to look like at all time. If not implemented
properly, the performance is extremely bad. The fact that we implement global
event delegation is not just a bonus, for some of our apps adding/removing
event listeners is actually the bottleneck.

4\. Bonuses: Having compatibility for IE8 and custom event plugins is actually
the cherry on-top of the cake. Since React has its own event management,
supporting those is pretty trivial and isn't a big amount of code.

We worked very hard so that React core is extremely small and doesn't contain
"other useful webapp stuff". We have a special addons section for that:
[http://facebook.github.io/react/docs/addons.html](http://facebook.github.io/react/docs/addons.html)

------
jashkenas
For an interesting bit of prior art, check out Reactive.coffee — an MIT
project that implements a reactive HTML DSL in CoffeeScript:

[http://yang.github.io/reactive-coffee/](http://yang.github.io/reactive-
coffee/)

... note that it's not related to Facebook's React — just shares the name, and
some similarities in surface area.

~~~
smrtinsert
Have you considered adding a macro system to cs? I think it would eliminate
the need for stuff like JSX which seems to get reinvented every so often.

~~~
jashkenas
Absolutely. There's a bunch of interesting work in that area:

[https://github.com/paiq/blackcoffee](https://github.com/paiq/blackcoffee)

[http://mrluc.github.io/macros.coffee/docs/macros.html](http://mrluc.github.io/macros.coffee/docs/macros.html)

[https://github.com/jashkenas/coffee-
script/pull/3171](https://github.com/jashkenas/coffee-script/pull/3171)

~~~
smrtinsert
Wow that's looking pretty nice already.

------
yahelc
This post made me realize how infrequently we've been seeing HN posts about
CoffeeScript in recent months, and the data seems to back that up:
[http://hntrends.jerodsanto.net/?q=coffeescript](http://hntrends.jerodsanto.net/?q=coffeescript)
I guess we've passed Peak CofeeeScript.

~~~
ash
Here's JavaScript vs CoffeeScript on the same graph:
[http://hntrends.jerodsanto.net/?q=javascript%2Ccoffeescript](http://hntrends.jerodsanto.net/?q=javascript%2Ccoffeescript)

Both have huge strange drop in Q3 2013. Problem with trends data?

~~~
yahelc
Yeah, I think you're right. A bunch of popular terms have precipitous,
unexplained drops in Q3. Still, even if you ignore Q3, there's still been a
drastic drop.

------
MarkPNeyer
having html inlined in your js code is ugly. so is mashing strings toether.

a friend at twilio pointed me to a library called JUP

[https://github.com/hij1nx/JUP](https://github.com/hij1nx/JUP)

it lets you express html as json. i don't understand why this isn't more
popular.

[ "div", { class : "a-class" }, [ "span", "you can use all of your json tools
to edit the DOM" ] ]

so awesome!

~~~
saraid216
> [ "div", { class : "a-class" }, [ "span", "you can use all of your json
> tools to edit the DOM" ] ]

So, that means

    
    
      <div class="a-class">
        <span>you can use all of your json tools to edit the DOM</span>
      </div>
    

, right?

~~~
flywheel
Probably, but that syntax is awful.

This syntax makes more sense to me:

[{"tagName":"div","class":"a-class","children":[{"tagName":"span","innerHTML":"you
can use all of your json tools to edit the DOM"}]}]

------
Bahamut
"It also breaks a rule that I've never cared for, where your templates are
supposed to be separate from your code."

This is not a "rule" that I came upon - I figured out how crappy having your
templates tied to your JS makes working with quickly on my own. It is an
absolutely ugly pattern.

~~~
catshirt
it's an ugly pattern when you're inlining function strings as HTML attributes.

if you can reference functions directly as attributes of your views it
actually makes perfect sense.

mixing your templates and your code sucks when you're working in 2 languages
across 2 files and you have to glue everything together. as soon as you're
writing them both to the same target it makes perfect sense. especially since
react still uses the DOM event model behind the curtains.

------
mavdi
My Reaction to CoffeeScript over time: CoffeeScript is Shit CoffeeScript is OK
CoffeeScript is Awesome! CoffeeScript is OK CoffeeScript is Shit

~~~
pspeter3
Exactly this. I feel more in love with TypeScript than CoffeeScript at this
point. I think that part of it is that ES6 is absorbing a lot of the need for
CoffeeScript.

------
davemo
If you'd like to use React + JSX + CoffeeScript I built a little workflow [1]
using Gulp that works pretty nicely; it takes advantage of the backtick
character in coffeescript compilation to let you do cool things [2].

[1] - [https://github.com/davemo/react-
battleplanner/blob/battlepla...](https://github.com/davemo/react-
battleplanner/blob/battleplanner/gulpfile.coffee)

[2] - [https://github.com/davemo/react-
battleplanner/blob/battlepla...](https://github.com/davemo/react-
battleplanner/blob/battleplanner/app/js/components/character_selector.coffee)

------
aboodman
The event handling was something I also thought I didn't want at first. It is
actually important though because it allows you to register event handlers
before the component is mounted in the DOM, which comes up all the time
because of the stateless abstraction React provides.

React's event handlers also serve as a unit under which state updates are
batched. In a normal DOM event state updates are performed synchronously. (It
seems like rAF should allow batching by default, but this hasn't been enabled
because of some edge-case related to unit testing that I don't quite
understand.)

------
vjeux
You can also use a more lispy way to write React code in CoffeeScript

    
    
        {div, h3, textarea} = React.DOM
        (div {className: 'MarkdownEditor'}, [
          (h3 {}, 'Input'),
          (textarea {onKeyUp: @handleKeyUp, ref: 'textarea'},
            @state.value
          )
        ])
    
    

[http://blog.vjeux.com/2013/javascript/react-
coffeescript.htm...](http://blog.vjeux.com/2013/javascript/react-
coffeescript.html)

------
cordite
Wow, that is really concise!

ClojureScript also has a nice react kind-of-thing (now named reagent), though
all the parenthesis makes it more noisy.

~~~
bslatkin
There's also Om:

[https://github.com/swannodette/om/wiki/Basic-
Tutorial](https://github.com/swannodette/om/wiki/Basic-Tutorial)

~~~
chc
There are three ClojureScript React libraries I've seen, each with a very
different approach.

Reagent is basically just a native interface to React.

Om sort of reimplements React on top of React with a few modifications to make
it more ClojureScripty (e.g. taking advantage of persistent data structures
for state).

Quiescent takes React and turns it into a simple rendering library, leaving
out any concept of state management.

------
jbeja
Learning Reactjs the last couple weeks really got me excited. I love the
philosophy about writing highly decoupled peaces to build a bigger one. Also i
discover that usin with coffeescript make the syntax very declarative, it look
like Kivy language.

~~~
mattgreenrocks
> I love the philosophy about writing highly decoupled peaces to build a
> bigger one

Glad to see people are wising up to the benefits of minimal coupling.

For too long, web development seemed to revel in being a pig squalor of
software engineering: "move fast and break things/just use Rails!/coupling
doesn't matter anymore!" It's probably the influx of new developers. I don't
mind that the mistakes are made as much as the collateral damage that occurred
to analysis and design phases.

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

Shameless plug here because author's site doesn't have comments: maybe you
could try Vue.js: it is <12k gzipped, support IE9 and above only, and even
less opinionated.

[http://vuejs.org](http://vuejs.org)

------
Gonzih
I was working with CoffeeScript for last ~2 years, nowadays I kinda prefer to
use plain old javascript with underscore library. No coffeescript, just got
tired of constant mental mapping between two languages.

~~~
elwell
huh, I don't find myself mental mapping to js. unless you're referring to
during debugging.

~~~
mistercow
If you're doing it during debugging, you might want to turn on source maps
instead.

------
hanthethanh
[https://github.com/lantiga/react.hiccup](https://github.com/lantiga/react.hiccup)
is also nice.

------
elwell
couldn't you curry out all those nulls?

------
andyl
Great post. I also use plain React (no JSX) with CoffeeScript. I've used
Ember, Backbone, Angular, but for me React/CoffeeScript is by far the most
productive.

~~~
jbeja
Remember that you can use Backbone Models and Collections and Router with
Reactjs only caring for events and views. They really work pretty well.

