Regarding the new architecture for Cycle.js I’ve been promoting, Nested Dialogues, Frederik Krautwald told me something that left me pondering:
Namespacing dialogues quickly becomes a nightmare. This is true when you need to place several dialogues of the same type in/on the same view/page. You need to come up with namespace after namespace. Definitely not a good solution IMO.
Namespacing of Nested Dialogues is a technique where each component is given a unique namespace which describes its complete ancestry. For instance, if I have a toggle button in a list in a tab, its namespace would be ['.tab-foo', '.list', '.toggle-button1']
, and its DOM selector .tab-foo .list .toggle-button1
should always match only one element when given to document.querySelector()
.
In theory, namespacing solves all problems related to ambiguity of selectors and global name collision, but as Frederik said, it becomes a tedious task to write and manually maintain them.
This is a problem fairly similar to global classnames used for CSS styling. Inspired by CSS Modules and how it allows you to work as if each component had its own scoped classnames (while under the hood converting each local classname to a global unique classname appended with a hash), I thought it might be useful to experiment with the same approach also for Cycle.js selectors.
Consider the Nested Dialogues example of a BMI calculator. It has labeled slider components, which take a name as argument:
The relevant parts are:
The default name is empty, which means you always need to give a name if you have more than one instance of this component in the application. For instance, in this BMI example, the BMI calculator uses two labeled sliders, one for weight and another for height:
Names '.weight'
and '.height'
are given.
It occured to me that we could use a randomly generated name instead of the empty string by default.
The default parameter is a new randomly generated name whenever the labeled slider component function is called. This way, we can use the labeled sliders in the BMI parent without specifying the namespace:
And it works.
The first problem that arises with that approach, though, it name collision possibilities. There are a couple of ways around that, though. One is through incremental ids:
And the other is using collision-resistant ids such as from cuid
:
Either way, the random name is optional. You can still explicitly provide the name, in case the parent component needs the child’s name. This use case often happens when the parent component has a list of components of the same type, and the parent needs to extract information about its state from the child’s id, such as in this advanced list example.
I’ll keep looking for patterns that simplify workflow in Cycle.js, while not sacrificing its explicitness (contrasted to “magic”).
Copyright (C) 2015 Andre 'Staltz' Medeiros, licensed under Creative Commons BY-NC 4.0, translations to other languages allowed.