Hacker News new | past | comments | ask | show | jobs | submit login
React, JSX, and CoffeeScript (neugierig.org)
123 points by bslatkin on Feb 13, 2014 | hide | past | favorite | 69 comments



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.


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.


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.


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)


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")]


Exactly what we do and it's eminently readable. Compnents as well end up looking good.

    div null,
      RadComponent onClick: @handleClick, model: @props.thing
and so forth.


I still have to do a mental mapping between this and what will ultimately be generated: HTML. Since JSX is XHTML, the mapping is minimal. This also moves in the direction of web components, which is getting a lot of attention from the Chrome team lately.

  <div>
    <RadComponent onClick={this.handleClick} model={this.props.thing} />
  </div>


I'm pretty sure that's mostly fairly simple habituation. This is something that's unfamiliar at first - though not hard - and that slows you down. But you'll get used to it really quickly.

Syntax like this tends not to matter very much in my experience. What does matter is syntax changes that affect order dependence, and particularly nesting. Those really require changing the way you think - replacing one symbol with another is just a matter of plain & simple practice.


I love destructuring in coffeescript, it's one of the best additions ever made to js.



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.


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

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


There's also brunch[1] and react-brunch[2]

[1]: http://brunch.io

[2]: https://github.com/darthapo/react-brunch


IMHO, that looks a little bit confusing, javascript or coffeescript are not lisp. But still i will give it a shot.


If you miss the parens, we can fix it:

    ({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")])))
However, assignments and binary operators are still infix, and comas are needed.

=^_^=


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.


I don't know if this is true for CoffeeScript, but sometimes an unfamiliar syntax enables something new and very useful. You won't experience that unless you embrace something very different.

eS6 will have fat arrows, but comparing CS today to JS today, I find the -> and => constructs are more than just an abbreviation, they make it radically easier to write AND READ code that makes heavy use of functions.

YMMV, of course!


It's inspired by a family of languages you probably aren't familiar with. My suggestion is to make simple projects with it, and start small using the features you clearly understand. Overtime you'll get more curious and want to learn the rest of the cool stuff and when you do you'll see ways to slash lines and lines of code from your old js or even cs.

There are better ways to learn new languages but cs is familiar enough to js that once you start coding you'll see it comes more naturally than you think.


The syntax we're talking about above is probably more confusing and less intuitive than 99% of the Coffeescript out there. Don't be alarmed. If you know Javascript, it's pretty approachable. The syntax is small, but all makes sense. You do need to give it a chance (read about it) and actually try it to really like it-- but that's true of any language. What's so amazing about coffeescript is such a small investment of time can payoff so strongly in terms of shrinking your code down it its purest, clearest form.


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


Very true.


I write and maintain a large program written in coffeescript every day, and you're right to be worried.

Coffeescript is has a much trickier syntax than javascript, and if you use it all the time, then the brevity and reduced line noise is a slight win, but you never lose the downsides of the complex syntax.

If you're wondering what's wrong with coffeescript, it basically boils down to the fact that the syntax is chock full of unexpected exceptions. I notice that I and everyone I know sometimes get's surprised by what a piece of syntax actually compiles to (not a good sign).

Some examples:

    f 
      a: 1
      b: 2
compiles to

    f({ a: 1, b: 2 }); 
but

    f
      o
fails to compile (so whether you can break an argument onto the next line depends on whether it's an object literal)

Comma's to delimit multi-line arguments are usually redundant, but not on the first line, so...

    f 1,
      2
      3
compiles to

    f(1, 2, 3)
but

    f 1
      2
      3
doesn't compile at all.

Curly braces aren't entirely optional, so

    o = [ {
       a: 1
       b
    } ]
compiles to

    o = [ { a: 1, b: b } ]
but

    o = [
       a: 1
       b
    ]
compiles to

    o = [ {a:1}, b]
      
Note that this is particularly tricky if you have functions with complexly nested arguments, which is what the original artical is proposing to use for React. In JS that would be messy but unsurprising, but in coffeescript you will run into the occasional miscompilation, especially since it's indent sensitive but very flexible with those indents.

For example you may start out with:

    a 
      href: uri
      span
        className: "x"
        "content"
...then decide to remove the class, and end up with...

    a 
      href: uri
      span
        "content"
But the second example actually nests the content inside the <a>, not the <span> (and it'll crash at runtime since span is passed rather than called).

You can even have code that compiles & runs and just returns the wrong value:

    div
      id: "foo"
      span null
        "content"
vs.

    div
      id: "foo"
      span null,
        "content"
Both result in valid JS that would run and display a valid DOM using React, but that comma changes the generated DOM. No warnings, no errors.

All in all, I'd stay away from coffeescript if I didn't already have a large codebase to maintain. There are a bunch of other, better, compile-to-JS alternatives to make JS syntax less onerous - so you can get coffeescript's advantages without the nasty pitfalls.


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.


(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.


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


Hey, another React team member here.

Yes, some compatibility code exists, but it's mostly just part of the default config which you can change. Additionally we don't inject things like the tap plugin by default so you aren't getting those extra bytes.

You can see what I mean about React's modularity by looking at this file https://github.com/facebook/react/blob/master/src/browser/Re...


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/

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


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.



Wow that's looking pretty nice already.


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 I guess we've passed Peak CofeeeScript.


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

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


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.


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

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!


This point comes up often when talking about React. You can still put the templates in your own file that the view requires and renders in the `render()` function. No different than Backbone.


Enyo JS (http://enyojs.com/), which powered the later versions of Palm/HP/LG webOS, expresses its components in JSON as well. Having developed both for webOS and now in React/JSX, I can say JSX is the more easily understood of the two. JSX does enforce small oddities like camel case attributes and always-closed tags since it is XML in the end, but it's closer to HTML and that is reason enough for it to win for me.


There is DOMBuilder with the same array/object syntax and also it has the function syntax too. It can be used server-side and in the browser too because it builds a virtual DOM and outputs HTML string or just uses the browser's native DOM elements. https://dombuilder.readthedocs.org/en/latest/


> [ "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?


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"}]}]


yes!


A coworker made something similar that we all appreciated using: https://github.com/nanotone/js-templates


But it's not. JSON doesn't have closing tags, which makes it more difficult to read. JSON is for machines.


The divs in jup are closed with ']'

edit: ahh, see your point. ']' doesn't say what it's ending.

i've never had that be an issue with proper use of whitespace and newslines, but i see your point.


"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.


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.


Actualy is not, IMHO we are not writing templates but components that are meant to be insolate with a single responsability in mind, not more. There, with can start building higher components combining the lower one to make the app, the Reactjs approach hold separation of concerns but in a different way that maybe you are not use to.

If the jsx syntax is what is holding you back is actualy optional like the Reactjs documentation clarify , you can use browserify or requirejs to separate the React.DOM objects in a diffirent file from your logic too. Cheers :)


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


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.


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...

[2] - https://github.com/davemo/react-battleplanner/blob/battlepla...


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.)


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...


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.


There are actually two main React libraries for ClojureScript, Om[1] and Reagent[2]. There are also other libraries for doing the templating if you prefer hiccup[3] or enlive[4] style templates, which I find a bit more readable than straight function calls.

Overall though, React seems to be shaping up as a great solution for building reactive templates that isn't tied into larger frameworks like the solutions within AngularJS or Ember.js.

[1]: https://github.com/swannodette/om

[2]: https://github.com/holmsand/reagent

[3]: https://github.com/r0man/sablono

[4]: https://github.com/ckirkendall/kioo



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.


Which is actually how you should use React to get performance benefits out of language, not just fancy syntactic sugar.


All the parenthesis does not affect noisiness in any way. Common misconception. They actually make things much cleaner and nicer.


Eh, I beg to differ, coming from the Haskell world.

I think what you are appreciating is the tree-but I may be wrong in my supposition.


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.


> 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.


"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


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.


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


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


Well, mostly during debugging. But not only.



couldn't you curry out all those nulls?


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.


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




Consider applying for YC's W25 batch! Applications are open till Nov 12.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: