Stupid riot.js tricks
I made a simple Riot.js application to try it out. My demo is as simple as it gets: it lets you add and remove items from a list, with a link to display an item from the list selected at random.
My initial reaction is mostly positive. It emulates many of the popular aspects of React.js, like component-orientation and automatic refreshes via virtual DOM, but with much better readability.
I designed my app as a single custom tag (component), since it’s all custom, and the two main sections (list management and random item selection) are both tightly coupled to the same state (the list). There is nothing here that I see as reusable. That approach worked great, until I wanted to add an animation when you select a random item from the list.
My first attempt was to make a new highlight
custom tag, which wraps the inner content and listens to the Riot.js updated
event:
<highlight><span>{foo}</span></highlight>
<higlight>
<yield/>
<script>
// script tag only for syntax highlight
this.on('updated', function() {
$(this.root).effect('highlight', 500)
})
</script>
</highlight>
Two problems with that:
-
the
updated
event will fire on any update within the custom tag. So updates because the list changed on add/remove will cascade to the nestedhighlight
tag. -
a new custom tag creates a new nested binding context, so an expression like
{foo}
that worked before now needs to be{parent.foo}
. That reminds me a little bit about how Knockout.js binding contexts work. Similarly, bindings for form inputs are pushed down to the child tag as well.
Now for the “stupid tricks” part to address these issues:
-
I introduced a new
inline-tag
that serves no useful purpose except to isolate update events. That saved me from having to introduce one-off custom tags likelist-management
, which would break the flow of the page and make readability worse rather than better. (I would feel differently if the components were reusable, or if the page was large enough that breaking it into smaller pieces improved readability.) -
I introduced a convention to qualify all references in template expressions with a
ctx
placeholder, and then assignctx
appropriately in each tag handler. In the main tag, I assignedctx = this
. In the placeholder tags where I want to automatically refer to the parent’s context, I assignedctx = this.parent.ctx
. That way, I can refer to{ctx.foo}
in an expression regardless if it’s wrapped withinline-tag
orhighlight
. -
For form inputs, which are bound on the current tag context, I added an option to export the bindings to the parent.
Purists will probably tell me that I should have made more custom tags rather than coming up with hacks to avoid them. Also, anyone who has spent more than the few hours that I have on Riot will probably tell me that there was a better way to handle animation effects. Still, I got it to work, and I feel like the end result is easy to read and understand.