comment 0

Riot.js gotchas

UPDATE (2/1/2016): Added info on opts object.

I’ve been exploring Muut’s Riot.js library over the past few days. For the most part, I really like it. It’s like a lightweight React with three key advantages:

  1. Tiny size (~10kb min’d and gzip’d)
  2. Custom HTML tags used for layout instead of JSX
  3. Very fast

While using Riot, however, I’ve noticed a few gotchas, or more generally features and patterns that are really important but aren’t explained well in the documentation:

  1. Mounted views vs. nested tags: When creating a Riot.js app, a reasonable strategy is to compose your views of nested custom tags. However–and this is really important–only the tag that you mount with riot.mount(tag) will be mounted. Sub-custom tags of your mounted tag will not be mounted, which is important because to make the custom disappear you have to unmount them. So, when creating an app, if you want to be able to use tags as interchangeable views, you must explicitly mount them so that they can be unmounted/remounted. A good pattern is to use a div with an id like #view as a mount point for your custom tag-based views (see next).
  2. You can “overwrite” mounts: If you keep calling riot.mount(‘#view’, ‘custom-tag’), passing in the same mount point each time, you will achieve the effect of swapping out custom tags/views on that mount point. There’s no need to keep track of each mounted tag and unmount before mounting.
  3. Put require() statements inside <script> tags: When requiring modules for your custom tags, place the require statements inside your custom tags’ <script> tags. Not only does this keep a tidier namespace, certain tools break when you place them at the top of the tag files (I’m looking at you, Karma).
  4. opts is a funny little creature: When you pass in a data object or attributes while mounting your tag (e.g., <tag custom-attr="val"> or riot.mount('tag', data)), the data/attributes gets saved to the opts object, which is accessible from inside your tag’s script as well as conditional attributes inside your tag. But opts is not your everyday object; specifically, it isn’t a copy of whatever you pass in. Opts is actually an empty object with no properties but whose prototype is set to the object(s) that you pass in during mount or via attributes. JSON.stringify(opts), for example, will consistently return {}. For the same reason, you shouldn’t reference opts directly inside of conditional attributes because it often doesn’t get dereferenced properly leading to strange results. The preferred idiom for handling conditionals based on opts is to assign your opts property to this and then reference the property that way. So instead of this:
    <tag>
      // Danger! This may not show because showMe
      // may evaluate to undefined no matter what you
      // pass in.
      <span if={opts.showMe}>Hello, world!</span>
    </tag>

    Try this:

    <tag>
      <span if={showMe}>Hello, world!</span>
    
      <script>
        this.showMe = opts.showMe
      </script>
    <tag>

     

Leave a Reply