Replacing <JSX />
(Part 1/3) - <JSX /> The Bad Parts
I’ve been using React since around 2014, but I never liked JSX, even with many small improvements and much better tools, I still try to avoid it where possible. After working with Clojure for a while, getting back to JSX was just too much for my brain, so I wrote a tiny library that implements some of this goodness found in Clojure (Hiccup) in javascript.
I decided to write this blog post (series) partly because I believe that all React developers can benefit from not having to deal with the horrors of XML, partly because I don’t want to look weird at hackathons, but mostly, because I believe that great ideas should be spread, and trust me, Clojure has plenty of great ideas, Hiccup is just one of them ;)
In the next 2 parts, I’ll try to do my best to show the benefits of using “data first” (da LISP way) approach as an alternative to JSX:
But first, let’s talks about (my) 3 major issues with JSX.
JSX brings compile time.
Some may argue that it’s not a big deal today (2017), as most JS projects add compilation (Babel) to their development workflow, and “webpack”… and flow, and all kinds of other fancy tools..
Still for me, adding a compiler to my workflow (not release) in such a dynamic language as JS just sounds like a poor idea. Maybe I can accept the compiler cost as a temporary workaround for missing features in the runtime.
Otherwise, I would rather get myself a better-designed language, or at least a better DSL for the same price! and it won’t be based on XML syntax!
Speaking of XML syntax…
JSX Is <Ugly> And <Verbose /></Ugly>.
I think that there is a reason why XML is not that popular today. XML sucks, It took a while for people to realize this, but today we don’t even use for XHRs! (XML HTTP Request), why should we use it in the heart of our apps?
Think of XML as Lisp for COBOL programmers.
We all know that there is a much better way to represent data in Javascript, it’s called Javascript.
Here’s what I mean by ugly:
// why write all this:
<Panel>
{this.props.items.map(item => <Item {...item} />) }
</Panel>
// When we just want this:
Panel(this.props.items.map(Item))
This is just one of many readability issues with JSX, but let’s focus on the most painful.
This one is very annoying:
<Content>
{
if (this.state.loading)
<LoadingMessage />
else if (items.length)
<ItemsList>{items.map(i => <Item key={i.id}>{i}</Item>)}</ItemsList>
else
<ItemsList><ZeroItems /></ItemsList>
}
</Content>
This is how I would “fix” it:
if (this.state.loading)
return <Content>
<LoadingMessage />
</Content>;
if (items.length == 0)
return <Content>
<ZeroItems />
</Content>;
return <Content>
<ItemsList>
{items.map(i => <Item key={i.id}>{i}</Item>)}
</ItemsList>
</Content>;
And at first it may seem like a good fix, but it’s not! because if you look closely enough, it’s clear that JSX just adds noise.
The above codes with pain JS function calls:
if (this.state.loading)
return Content(LodadingMessage());
if (items.length === 0)
return Content(ZeroItems());
return Content(ItemsList(items.map(i => Item({key : i.id}, i))));
Also what if instead of only 1 level of wrapper elements, you have 4?
<Parent>
<Wrapper>
<Wrapper>
<Content>
//....
</Content>
</Wrapper>
</Wrapper>
</Parent>
Here with JSX, you will end-up with a lot of duplicated JSX code or inline ifs (pick your poison…).
When I had this issue, I used helper functions, and smaller sub-components, basically I was just moving my logic around in attempt to avoid JSX.
It’s also possible to use JS function calls in the deeply nested
example by using function composition (_.compose
, etc..), and I’ll show you a better method in part-2.
But my point here is that all we get from JSX is the ugly and annoying <></>
noise,
and we pay for this “luxury” by adding an external, compile-time DSL.
This is ridiculous.
JSX Is Not Extendable.
Because JSX is an external tool, it’s not easy to define your own shortcuts and helpers, and it sucks big time!
One common example is the className
property, as almost every project uses a function to “compile” an object (or array) to a string, this has to be done for each className
, so it very quickly becomes noisy and extremely annoying.
Look at this:
// in many projects, I want this:
<div className={classNames({foo: true, bar: true})} />
// to be this:
<div className={foo: true, bar: true} />
Another example is if we want to add a “hidden” property to all components that will hide them when true (preferably by returning null). This may serve as a cleaner alternative to wrapping in ? :
, or worse, using &&
and introducing hard to catch bugs.
Here:
// ugly & verbose:
items.length ? <List>{items}</List> : null
// broken! - (0 && true) will eval to 0!
items.length && <List>{items}</List>
// a little better but sill ugly!
(items.length > 0) && <List>{items}</List>
// what we actually want:
<List hidden={items.length < 1}>{items}</List>
Currently, to achieve this sorcery many projects use a wrapper component
(same for adding a type="button"
prop). This sucks big time! because it means that I need to add complexity by wrapping each
component, every time, and I need a way to detect when a red-eyed developer (probably me) is making a typo at 3AM…
damn… if I could just tell JSX to transform props for me..
Again, I’m not arguing that JSX should support those features, but rather that JSX should allow the users to define such shortcuts where it makes sense to them. I believe that this alone can actually solve a lot of the “higher order component” mania.
Now What?
First, let me just say that It’s OK if you don’t agree with my opinion on JSX.
I tried my very best to avoid straw-man arguments, But I possibly probably made some due to my own biases and ignorance.
So feel free to correct me, and/or suggest counter arguments.
To clarify I don’t think JSX is the work of the devil (like say Angular :P). I just think that JSX is redundant at best, and we can simply use plain Javascript to achieve better readability. Even using simple function calls is better! but as you probably already know, I believe that there is an even better method.
Next, we will explore this “better” method, by playing with Clojure and Hiccup, then in part 3, we will apply our new knowledge back to javascript, and make our render() functions pretty again!
(load :comments)load