greek

0.2.0 • Public • Published

greek

An idiomatic / DSL-based alternative to using JSX

Disclaimer

If you are new to react, I heavily recommend using JSX. There is great documentation, almost all examples you will find online are using jsx. Using greek when you are still trying to grasp what React is all about will only add to your confusion and frustrate you. I know, because I made that very mistake myself. :-)

Also, as you may have guessed from the pre-1.0 version number: things my change in all kinds of incompatible ways. I am still experimenting with this.

Install

The usual npm install greek should do.

Usage

DSL = require "greek"
{divh3ulli} = DSL
  builtins:Object.keys(React.DOM)
  createElement:React.createElement
  isValidElement:React.isValidElement
 
MyCustomComponent = React.createFactory require "my-custom-component"
 
renderSomething = ->
  div className: "example",
    h3 "Have a list:"
    ul [
      li "first item"
      li "second item"
    ]
    MyCustomComponent foo: bar / 2

My motivation for doing this

There is -- at least in my opinion -- nothing wrong with JSX. In particular, if you are a happy ES6 user, stick with the Babel transpiler, it should cover pretty much all you will ever need and more.

I prefer CoffeeScript over Javascript. I know there are a couple of attempts of doing something like JSX with Coffeescript, but then again: why? After all, JSX compiles down to simple javascript. If you play around with the Babel "REPL", you can see that the stuff that looks like HTML simply translates to an isomorphic structure of nested calls to React.createElement. It is quite readable, even in Javascript, and even more so if you use some short-hand name instead of React.createElement. I used E here:

E("div", { className: "example" },
  E("h3", null,"Have a list:"),
  E("ul",null,
    E("li",null,"first item"),
    E("li",null,"second item")
  ),
  E(MyCustomComponent, { foo: bar / 2 })
);

Or, using coffeescript:

"div"className: "example",
  E "h3"null"Have a list:"
  E "ul"null,
    E "li"null"first item"
    E "li"null"second item"
  E MyCustomComponentfoo: bar / 2

This is not bad, but we can do better.

  • Is it really necessary to use string literals?

  • Maybe we can get rid of the E functor?

  • Having to write null if there are no attributes kinda sucks. Surely there is another way?

Clearly, we need to do some meta-programming. For all the standard HTML elements we can predefine functions that behave like partially evaluated versions of React.createElement. And this is basically what the greek does.

Rough Edges and Limitations

The above example basically show what the greek module does right now. I like it, but it is not perfect.

VarArgs-Syntax for children

If you look closely at the above example you may realize something odd: Both, the div-node aswell as the ul-node have more than one children. In the first it is possible to use a vararg notation, but in the latter we need to enclose the children in an array. This is due to a somewhat odd property of the coffeescript syntax. (Or at least I think it's a bit odd, there may be reasons for implementing it this way). Most unfortunately, if you leave the square brackets away the whole thing still gets parsed by coffeescript, but not the way you want: the li-nodes become sibblings of the ul instead of children. This can be avoided by enclosing the lis with parentheses, but clearly, this is a problem. It is very easy to get this wrong. I therefor think it would be best to drop the support for varargs altogeteher. But I haven't completely decided yet.

Custom-Components require special treatment

In the above example, we cannot simply use the custom component, but instead we need to wrap it using React.createFactory. This makes sense, if you think about how the construction and rendering of the virtual dom works. In the "normal" case, React.createElement takes care of this; note how the component is not called directly but instead given to React.createElement. This must work a bit different in our case, since we decided to remove the React.createElement functor from our DSL in the first place.

It is not that bad once you realize what is going on. But when you are new to React, this is very confusing, and it gets even worse if you are are working with higher order components.

Since 0.2.x: the "unfancy" API

Mostly due to the two abovementioned problems, I started experimenting with a compromise: It re-introduces an extra functor similar tocreateElement and only adds some sugar that allows us to ommit the ugly null when there are no attributes. This buys us some convenience when dealing with custom components: we do not have to wrap them with createFactory first.

Another thing that is different: the syntax for children is less lenient. When there is more than one child it must be put into an array.

Breaking Changes in 0.2

I mentioned this is pre 1.0, right? Since the 0.1.x versions, I had a look at a couple of other UI libraries besides react that also use a virtual dom. It seems that most use very similar APIs to create virtual dom trees. I decided I do not want this module to depend on react. So now to use the Greek DSL, you need tell it how to use your UI library. See example at the top.

Previous Work

I started work on this after reading the following blog posts:

The author of the second one created the module react-coffee-elements which works very similar to mine. But I had to try it myself to realize this.

Package Sidebar

Install

npm i greek

Weekly Downloads

3

Version

0.2.0

License

GPL-2.0

Last publish

Collaborators

  • l.degener