Re: [w3ctag/design-reviews] [WebComponents] Custom state pseudo class (#428)

There are few things puzzling me regarding this proposal, and I'd like to list all points in here.

### The meaning of a `state`

Even in the HTML world, components states can be defined either via classes or attributes, where attributes accept (at least) strings.
Using a DOMTokenList to cover an element `state` seems like a very poor choice, especially if there is already a plan to allow `:state(mode=collapsed)` in the future.

Most used frameworks though, use `state` as basic JS object/map, allowing (at least) serializable data to be part of the state, so that while I agree the state should be handled internally, the developer experience I'd expect from this proposal would rather look like:

```js
this._internals = this.attachInternals({status: "initial", uid: Math.random()});

// at any point in time ... updates *only* status, keep uid as it was
this._internals.update({status: "update"});

// at any point in time ... retrieve a value associated with the state
this._internals.get("status"); // "update"
this._internals.get("uid"); // the random number as string

// at any point in time ... replace the whole state, losing uid
this._internals.replace({status: "uid-less"});

// at any point in time, know if a state is set
this._internals.has("uid"); // false

// same as previously mentioned, for example purpose
this._internals.get("status"); // "uid-less"
this._internals.get("uid"); // null

// at any point in time, update a single entry of the state
this._internals.set("uid", Math.random());

this._internals.has("uid"); // true
```

Keeping the state in sync at once, and through an object, would make states handling per element much easier, and similar to what common frameworks to, only state keys that changed value should result into a CSS call/update/repaint

**Benefits**
  * instead of bloating the class with every possible `get thingy()` and `set thingy(...)`, components could have a single `set state(obj) { this._internals.update(obj); }` and play well with template literals/JSX based components
  * states are basically `Map` with constrains on the key (DOMString) and eventually constrains on the value, but once we have map, such constrain on values could also be dropped, as developers don't use only strings with their data


### About not allowing JS functions for states

This from @annevk is an interesting one:

> I'm not enthused at all about running script while selector matching

The current proposal bases CSS info on JavaScript classes getters (`get checked` or `set checked`), meaning that `:state(checked)` will **always** invoke a function, so that I don't see, within the current proposal, why functions would not be allowed, when to retrieve states we need to define a function (accessor) anyway. However, my previous proposal would get rid of this issue all-together, because you don't need to expose getters and setters per each private state key, so the running function, within the CSS scope, happens only when, internally, the engine decides it should run, without touching JS at all.


### About ditching built-ins Elements

Despite what WebKit does, there are numerous projects based on Custom Elements built-ins, plus the majority of VDom based frameworks don't care much about Custom Elements.

Confining this functionality only in classes that extend `HTMLElement` means this will have an incredibly slow adoption/value per effort ratio:

  * it's impossible to polyfill at runtime
  * if pre-processed, it will bloat CSS duplicating every `:state(...)` check via classes (and it will likely poison `attachInternals` native prototype method to be backward compatible)
  * frameworks not using Custom Elements (React, Vue.js, Svelte, etc) won't be able to adopt this solution for anything at all

Using the suggested approach with _Map_ **could** make this feature more broadly usable, and partially polyfillable, without needing to introspect classes, specially because classes might define `this._internals` as `this.#internals`, without exposing all keys handled by the internal class field.


### About using classes and the "collision" argument

What this proposal would like to solve, seems to be an issue nobody using frameworks really have.

Moreover, it's not clear how exposing `this.attachInternals()` would solve conflicting states, as I believe `el._internals = el.attachInternals()` would be done at any time, as well as defining getters and setters at distance, either on the element, or its specific class prototype, if it's a custom element.

As summary, I am not sure this specification, as presented, would really solve something concretely for Web FE developers, but I'd be happy to know more about the clear broken use case this feature would like to fix.


Best Regards

-- 
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
https://github.com/w3ctag/design-reviews/issues/428#issuecomment-565408602

Received on Friday, 13 December 2019 11:30:51 UTC