ponyfoo.com

React, JSX and ES6: The Weird Parts

Fix

I’ve spent a few days working with JSX and React and I have mixed feelings about them. React is pretty neat, but I find that they made some very unusual choices when it comes to their API design. Then there’s JSX, definitely the weirdest aspect of React – we’ll look into it as well. I’ve really enjoyed the ES6 and Babel experience, although I’ve noticed that there’s a learning curve where you start to decide whether using an ES6 feature is better than its ES5 equivalent or not, something we’ll explore towards the end of the article.

1961 Buick "Flamingo" with rotating front seat
1961 Buick "Flamingo" with rotating front seat

JSX is the new XHTML

Web-oriented template engines typically consist of an entirely new language Jade, Dust.js, etc – or a few extensions on top of HTML Mustache, Hogan, Nunjucks, etc. We rarely see hybrids like JSX, a language that’s more or less aligned with XHTML but which also comes with some gross limitations in what you’re able to do with it, alongside “cool” features like being able to mix it with JavaScript code. In that last aspect, it’s sort of like Jade, which you can also mix with JavaScript, but I’ll admit that mixing JavaScript and JSX is way cleaner than mixing JavaScript with Jade.

React being one of the communities that push ES6 most aggressively, I would’ve liked to see them implement views largely based around ES6 string interpolation. Of course, JSX was created so that you don’t have to use React’s virtual DOM API, which can get very verbose with a method call for each DOM element. But still, it would’ve been nice if they took a more ES6ish approach. And boy is JSX weird. In fact, the point about DOM elements brings me to an unfortunate side effect of JSX.

Unexpected and automatic DOM element insertion

As a newcomer to React, the fact that this:

<span>foo {'bar'} {'baz'}</span>

Is turned into that:

<span data-reactid=".1gm29bnrabk.1.0">
  <span data-reactid=".1gm29bnrabk.1.0.0">foo </span>
  <span data-reactid=".1gm29bnrabk.1.0.1">bar</span>
  <span data-reactid=".1gm29bnrabk.1.0.2"> </span>
  <span data-reactid=".1gm29bnrabk.1.0.3">baz</span>
</span>

Makes absolutely no sense to me. I’ll just go ahead and assume that this makes the life of whoever maintains the virtual DOM implementation in React easier, but they should’ve considered using text nodes. You might think this is not a big deal, but it is if we’re styling <span> elements in a certain way. In particular if you just expected the span you actually wrote to be the only <span>, and defined a style such as font-size: 1.1em, or even padding: 5px.

Besides, isn’t the whole point of React to avoid as many DOM operations as possible?

Using conditionals in your view components

Whenever you’re dealing with dynamic representations of data, particularly optional user-entered information such as the metadata about a product in an e-commerce application, you’ll want some sort of way to conditionally render a component.

For some reason, React’s JSX make this unnecessarily complicated by not being able to use if statements inside code blocks. The advertised solution is to place your conditionals outside of the template, in the component’s render method. Here’s a piece of code showing the coding style they recommend.

var loginButton;
if (loggedIn) {
  loginButton = <LogoutButton />;
} else {
  loginButton = <LoginButton />;
}

return (
  <nav>
    <Home />
    {loginButton}
  </nav>
);

Note that, in case you just wanted the if leg (no else), you could leave loginButton as undefined and nothing would be rendered. The problem here is that you end up having to hoist parts of your component for no reason other than what is, plainly put, a limitation of the JSX language.

A (still terrible) workaround that allows you to place conditional logic into the template is to use binary operators. For some other reason, those are supported by JSX. Of course, this is sad to look at, slightly confusing, and not as clear as plain if / else would have been.

return (
  <nav>
    <Home />
    { loggedIn && <LogoutButton /> || <LoginButton /> }
  </nav>
);

Declaring a doctype

Apparently declaring a <doctype> is impossible when it comes to React. I ended up with an unimpressive '<!doctype html>' + layout concatenation. This would be fine if we were just doing client-side rendering but we’re trying to get to a shared rendering application here, kind of the whole point of using a library like React.

Concatenating the <doctype> on the server-side will have to do for now. Luckily, it doesn’t break the diffing algorithm when the client-side code boots the application state.

Declaring HTML comments

HTML comments are similarly hard to shove into a JSX template. Granted, this isn’t something you want to add to a document very often, but it’s still important to be able to declare some when you actually need to. If you want HTML comments in your JSX very badly, you can use the method below (derived from this blog post).

var comments = `<!--[if lte IE 8]>
  <script src="/js/html5shim.js"></script>
<![endif]-->`
<head dangerouslySetInnerHTML={{__html: comments}} />

Which, OBVIOUSLY brings us to my next point.

React’s “smart” dangerouslySetInnerHTML API

I definitely can relate to the notion of attempting to help your consumers not to make mistakes, but this API borders on pompous and presumptuous. The documentation rightly asserts that misuse of unescaped HTML might result in XSS vulnerabilities and that you should sanitize user input. A public API method’s name is not the place where to tell people everything that could go wrong with it, though.

Angular used to have a similar ng-bind-html-unsafe, which at least wasn’t as presumptuous. It has since been deprecated in favor of ng-bind-html, which is a saner version of React’s implementation. When passed a string, it’ll become sanitized, and if you want it to be unescaped you just tell it through their $sce.trustAsHtml(string) service. Of course, in that respect it might’ve been better to just let the user pass in something like { unescaped: 'some <strong>html</strong' } instead of having to go through a service, but it’s good enough – at least it’s not as condescending, and I don’t feel like throwing up whenever I’m using the API.

Components coupled to client-side code and ES6

Another big issue in my understanding of React is the way how components are loaded. Pretty much the entire React codebase is loaded on the server-side, and that means that a lot of libraries which are meant for the client-side of your application will be loaded on the server. This becomes an issue whenever you are loading a module that accesses the DOM before you even use it, – usually happens when a library does feature testing to decide the API they’ll export – as that’ll result in a thrown exception on the server-side (as there is no DOM to be accessed).

You could work around this by using require statements instead of import, and requiring those client-side-only modules on a method like componentDidMount, which only get executed in the client-side. ES6 import statements need to be defined at the top level of your module definition, though, which means you can’t work around this one using plain ES6.

This can be really problematic as even a test like modern = !!window.document.addEventListener would break on the server-side, and there’s plenty of client-side libraries that do this before you call any methods on them.

Wrapping Up

I’ve yet to play around with redux and client-side routing – two big things that I’ll be playing with next and which might change my mind about a few of the points I’ve made here. I’ve only played around with React lightly, so take the article with a grain of “React onboarding experience areas of improvement” salt.

The new site is now up at bevacqua.io, and while I definitely didn’t need to use React to put it together, it’s always interesting to try out new pieces of front-end technology. I don’t only have ranty things to say about JSX, and I’m sure it’ll grow on me as I use it for more stateful applications and combined with redux.

ES6 has been fun so far, and I find myself gradually adopting some of the language features, as well as writing less semicolons. What’s hard sometimes is to decide when to use an ES6 feature such as arrow functions over a named function declaration. In general I’ve been taking the approach of defaulting to the ES5 alternative, because I don’t want to be littering my code with ES6 expressions just for the sake of it.

As a fun fact, here’s a snippet of code I felt happy to write using lots of ES6 and the experimental :: ES7 function bind syntax. Usually there’s lots of named method involved in this sort of code, or async.apply (or contra.curry), and ES6 definitely made my code cleaner on this one.

concurrent({
  repo: next => query('/repos/' + repo, next),
  master: next => query('/repos/' + repo + '/branches/master', next)
}, ::this.pulledRepo)

By the way concurrent is a method from contra, equivalent to async.parallel.

Yay!

Liked the article? Subscribe below to get an email when new articles come out! Also, follow @ponyfoo on Twitter and @ponyfoo on Facebook.
One-click unsubscribe, anytime. Learn more.

Comments (37)

tinkertrain wrote

For conditionals in JSX, you can also use the ternary method:

{ isValid ? <Component1 /> : <Component2 /> }

if what you want is to render nothing if false, return null:

{ isValid ? <Component1 /> : null }
Nicolas Bevacqua wrote

This syntax is just as bad as using && in the second case, and only marginally better in the first one

Guillaume Lecomte wrote

Do you prefer something like that? https://github.com/romac/react-if/

I don’t see type of conditional syntax would require modifications of JSX.

Nicolas Bevacqua wrote

Nope, I’d rather use something like do, I guess.

Jim wrote

You could just write a conditional function cond(condition, passResult, fail result). I have no problem with ternaries in my jsx though.

kentor wrote

Yeah I don’t really understand ternary phobia. It’s part of javascript. It’s part of the spec. The problem with using cond() or react-if is that there is no logic short-circuitry.

Roman Boiko wrote

Would you like something like this: https://github.com/reactjs/react-future/issues/35?

Roman Boiko wrote

Oops… Didn’t notice that you’ve mentioned the do expressions already.

Jos de Jong wrote

Nicolas, you mention a few valid points, though the first two “issues” you mention are really pernickety. “newcomers” will probably never come across this, and there is probably a good reason for it. As for your second issue: how about using a conditional operator…?

{ loggedIn ? <LogoutButton /> : <LoginButton /> }

I would really love to be able to use comments in JSX, that’s a shortcoming I regularly come across myself.

If you don’t want this article to be a low, half-informed rant and act in a constructive way instead, you could open a few issues at the React project to share your ideas for improvements and to hear the reasons behind certain choices.

Kenneth Chung wrote

{/* This is a comment in jsx. */}

Nicolas Bevacqua wrote

Sure, but that’s a JavaScript comment, not an HTML comment.

Andrew Smith wrote

But JSX is JavaScript, not HTML

Nicolas Bevacqua wrote

You’re ultimately using it to declare HTML, even though it’s through a DSL on top of JavaScript.

Nate Ross wrote

Yes, but JSX gets compiled down to JavaScript in the end. It doesn’t look much different than jQuery DOM manipulation.

kentor wrote

You’re using JSX to declare the DOM, not the HTML.

Nicolas wrote

“JSX is JS, not HTML”, and “JSX produces DOM, not HTML” are in fact the two main points that you failed to catch. It’s a very common, and hard to get through, misconception for React beginners. It took me a while to accept and understand this, and it suddenly all makes sense when you do :)

Mark Gaucher wrote

In regards to your point Unexpected and automatic DOM element insertion, attaching styles directly to HTML elements is generally bad practice. If you need to style a specific element you’re better off wrapping it in a span and explicitly assigning it a class. You could also try one of the many plugins that allow you to insert styles directly into your React code.

That being said, I do agree that React can spit out some interesting DOM elements.

Nicolas Bevacqua wrote

I was just mentioning one of many potential issues with unexpected DOM element insertion. If anything, this should be more clearly documented.

Skyler Nelson wrote

The fact that React’s views are not based on string interpolation is a pretty critical part of the good design of the system.

I think that you’re coming at React from the perspective of someone who’s rendering mostly static pages on the server side. It’s really nice that React can render on the server but it’s a client-side library first and foremost, and a lot of its design decisions make more sense if you’re thinking about it that way.

In particular, server side folks tend to think of the DOM in terms of the string representation that ends up getting sent out by the server - that is the DOM that they have to care about. But from the client side, the DOM is actually an API. Any XHTML string you end up with has to be dealt with in a two step process: it has to be parsed in one way or another and then that has to be translated into a series of DOM API method calls. You’re going to end up with at least one method call per DOM element no matter what, but that’s relatively invisible when you’re constructing things on the server side because that parsing and those method calls are all done by the browser automatically.

If you find yourself in possession of an XHTML string on the client side, the only straightforward thing you can do with it is dump it in to innerHTML. Again, the browser has to parse it and make its own DOM API calls at that point, so that overhead hasn’t been avoided. But that’s also unsafe (as you know) and it also destroys the state of any previously existing elements at that point in the DOM and it makes all of the clever things React does with shadow DOM impossible.

Shadow DOM is really the key feature that makes everything that React does well work, and for that to work you have to make an additional method call for each DOM element to wrap the DOM API calls (i.e. the call that renders the element to shadow DOM). If what you end up with at the end of your render method is an XHTML string, the library would then have to parse it into shadow DOM method calls every time you called render at runtime.

Not having to do that is pretty much the main advantage of React and the thing that motivates most of JSX’s design. JSX is about eliminating that parsing step and jumping directly to the method calls that end up generating shadow DOM. JSX maps to React shadow DOM generating method calls in an extremely straightforward way (modulo certain issues like the weird whitespace spans that you point out) and in turn those shadow DOM methods map on to the DOM API pretty straightforwardly. You can write in a mostly XML syntax with JSX but get out of doing any XML parsing at runtime with JSX because the translation from JSX to plain Javascript is so straightforward that it can be done at compile time.

So some of the seemingly quirky features of JSX are there specifically to enable that straightforward translation, and JSX/React as a whole makes more sense if you’re thinking of its design from the client side: on the server side, rendering happens once and dead ends in a string of XHTML, but on the client side, rendering might be happening many times a second and it dead ends in DOM API method calls - ending up with an intermediary XHTML string to parse somewhere in this process is the last thing we want.

(That’s not to say that JSX couldn’t be improved in certain ways - despite the advantage of the conceptual simplicity of it, it could handle comments better and some sugar for conditionals might not hurt.)

Skyler Nelson wrote

Another way of putting some of that is this: remember that JSX is not a template language, it’s XML-themed syntax sugar for Javascript.

Nicolas Bevacqua wrote

One of the most important aspects of React is being able to render on both the server-side and the client-side using the same code. I don’t see how that makes my post strictly about server-side rendered React.

Also, get your own blog! :)

Skyler Nelson wrote

Yeah, server side rendering is surely important. I’m just saying that some of the things you say, e.g. “I would’ve liked to see them implement views largely based around ES6 string interpolation” make a lot of sense from the server side, and not very much sense from the client side - rendering to a string is the default behavior when you’re rendering on the server, but avoiding that is like the thing that makes React performant on the client side.

Sorry I’m so verbose, “I have only made this letter longer because I have not had the time to make it shorter." :)

Zach wrote

Correct me if I’m wrong, but I couldn’t get past the fact you keep saying “Shadow DOM”. To my understanding, the virtual DOM !== Shadow DOM.

Shadow DOM is native to browsers and is the thing that Web Components and frameworks like Polymer take advantage of. Virtual DOM is just a copy of the DOM that is used to virtually diff against for faster performance, which is what React does.

Skyler Nelson wrote

You are correct, I was just mixing the terms up, thanks.

Dylan Piercey wrote

I agree with pretty much all of your points regarding react. It seems very opinionated with stuff like “dangerouslySetInnerHTML” and forcing you to use Dom attributes instead of HTML attributes. The span issue is another big one that just strikes me as lazy.

I am currently building my own little virtual DOM that fixes every issue you mentioned (except comments which I’m thinking about). Hopefully I’ll get to switch to it in production soon :).

Nicolas Bevacqua wrote

It’s awesome that you’re experimenting with this stuff. Best way to learn!

John Doe wrote

I think you need to give it some more thought.

Span vs text nodes: it would be a huge mess to navigate the text nodes. the span allows vdom to target a specific element to update efficiently, and you shouldn’t be styling generic elements anyway, it’s bad practice (for that exact reason).

hoisted if: that is a choice. you can have an

doctype: this makes no sense. just use the same html skeleton on client and server, it is illogical to ‘render’ a doctype through javascript - you only have javascript after the doctype has been parsed…

DOM on the server: you should only share the components themselves with the server, not the entire codebase…

Zach wrote

Just thought I would share this. I use a SIAF for conditionals inside JSX when ternary operations just don’t cut it:

render() {
  return (
     <div>
        {(() => {
          if (something) {
            return <Component />
          } else {
            return <Component2 />
          }
        })()}
      </div>
    )
  }
Nicolas Bevacqua wrote

Is that Lisp?

Roman Boiko wrote

Actually, I like this (arrow) syntax more than similar (function() { if (something) { return null; } })() expressions.

Alternatively, Babel allows us to use the do expressions proposal, but it may end up not being accepted. Example:

<div>{if (something) <Component /> else <Component2 />}</div>
Roman Boiko wrote

Sorry, hurried a bit

<div>{do {if (something) <Component /> else <Component2 />; }}</div>
Jeremy wrote

Amen to half this blog post (all the server-side JS stuff wasn’t relevant to me). The lack of conditionals in React is such a glaring, obvious failure that it makes the whole library look bad for leaving out something so obvious. It’s like “hey, you had Handlebars templates, and they were great, but now there’s JSX and it’s even better!!! … well except that JSX can’t do for loops, or any other kind of iteration, or if statements or any other conditional (unless you count fugly ternaries in the middle of your HTML), or with statements, or …”

I’m looking in to trying this though: https://github.com/valtech-au/jsx-control-statements

iofjuupasli wrote

React, JSX and ES6: The Parts

I’ve spent last year with React. And all the time I feel like I’m cheating. Facebook created something new by breaking some established principles. It gave us nice basis for further research and development. I see that there are many new libraries that inspired by React, and they are very promising.

JSX

Please don’t use JSX! Just compare it with hyperscript (or JSnoX)

JSX have only one advantage - HTML-coder (designer?) shouldn’t spend 10 minutes to learn hyperscript. Also it makes difficult to copy-paste HTML, but I see it as advantage.

Unexpected and automatic DOM element insertion

Yes, sudden span can break something. But you shouldn’t use styles on spans. If you do, you’ll have the problems not only with React.

As workaround this code

<span>foo {'bar'} {'baz'}</span>

can be replaced with (you can do it with JSX, but as I encouraged to use JS):

h('span', null, 'foo ' + 'bar' + ' ' + 'baz')

Using conditionals in your view components

Just use JSnoX. And there will be no questions like this. JSX is simple, but I found that it creates a lot more questions.

Also JS doesn’t force you to compile code. So you can just use React as any another js-library.

No setup, no build time, etc.

Declaring a doctype

Don’t render entire page with React.

If you do that, it means that your entire page should be controlled only with React.

You will have troubles with all libraries and plugines that adds something to body.

Modules for modals, notifications, feedback plugins, browser extensions, etc…

You shouldn’t fight with something that can be easy avoided.

Keep everything in its own places, isolated, simple.

Declaring HTML comments

There is one case in which you can want HTML comments - <!--[if lte IE 8]>.

But if you will follow my previous advice, probably you will have this comments outside of React.

dangerouslySetInnerHTMLIfYouActuallyKnowThatItCanNotBeHackedAndThinkTwiceBeforeUseIt

That’s the bad part. Can’t get why developers of React think that I’m so stupid.

Components coupled to client-side code and ES6

I think this problem relates to any isomorphic code.

Luiz Augusto wrote

I’m using ES6 with Babel for one month now and I’ve been into the same dilemma about using arrow functions. My final decision was to use it only in two situations: when you can make a one line statement or when you need the lexical this of course. :)

Roman Boiko wrote

React v0.14 will have DOM-related functionality split into a separate react-dom package. With Babel, it is quite easy to compile JSX to virtual-dom hypernodes. So, it would be interesting to try replacing react-dom with (some adapter over) virtual-dom. This could make it possible to eliminate data-reactid attributes and span insertion, and might also improve performance.

Miles Johnson wrote

I simply cannot fathom why you are building the entire HTML document using React, like the <head>, and the <doctype>. React should be rendered to a single element, usually a div, within <body>.

Zak wrote

Exactly what i thought!