| This might be heresy to many JS devs, but I think 'state' variables are an anti-pattern. I use webcomponents and instead of adding state variables for 'flat' variable types I use the DOM element value/textContent/checked/etc as the only source of truth, adding setters and getters as required. So instead of: /* State variables */
let name;
/* DOM update functions */
function setNameNode(value) {
nameNode.textContent = value;
}
/* State update functions */
function setName(value) {
if(name !== value) {
name = value;
setNameNode(value);
}
}
it would just be akin to: set name(name) { this.nameNode.textContent = name }
get name() { return this.nameNode.textContent}
/* or if the variable is used less than 3 times don't even add the set/get */
setState({name}){
this.querySelector('#name').textContent = name;
}
Its hard to describe in a short comment, but a lot of things go right naturally with very few lines of code.I've seen the history of this creating spaghetti, but now with WebComponents there is separation of objects + the adjacent HTML template, creating a granularity that its fusilli or macaroni. |
• Using DOM attribute or text nodes limits you to text only. This is, in practice, a very big limitation. The simple cases are Plain Old Data which can be converted losslessly at just an efficiency cost, like HTMLProgressElement.prototype.value, which converts to number. Somewhat more complex are things like classList and relList, each a live DOMTokenList mapping to a single attribute, which needs unique and persistent identity, so you have to cache an object. And it definitely gets more intractable from there as you add more of your own code.
• Some pieces of state that you may care about aren’t stored in DOM nodes. The most obvious example is HTMLInputElement.prototype.value, which does not reflect the value attribute. But there are many other things like scroll position, element focus and the indeterminate flag on checkboxes.
• Some browser extensions will mess with your DOM, and there’s nothing you can do about it. For example, what you thought was a text node may get an entire element injected into it, for ads or dictionary lookup or whatever. It’s hard to write robust code under such conditions, but if you’re relying on your DOM as your source of truth, you will be disappointed occasionally. In similar fashion, prevailing advice now is not to assume you own all the children of the <body> element, but to render everything into a div inside that body, because too many extensions have done terrible things that they should never have done in the first place.
It’s a nice theory, but I don’t tend to find it scaling very well, applied as purely as possible.
Now if you’re willing to relax it to adding your own properties to the DOM element (as distinct from attributes), and only reflecting to attributes or text when feasible, you can often get a lot further. But you may also find frustration when your stuff goes awry, e.g. when something moves a node in the wrong way and all your properties disappear because it cloned the node for some reason.