jorgebucaran / superfine

Absolutely minimal view layer for building web interfaces
https://git.io/super
MIT License
1.57k stars 78 forks source link

What if: class components? #135

Closed jorgebucaran closed 6 years ago

jorgebucaran commented 6 years ago

About class components (https://github.com/jorgebucaran/superfine/issues/134), I was thinking, what if we could do something like this?

import { h, Super, patch } from "superfine"

class Hi extends Super {
  state = { say: "Hi" }

  render = () => (
    <h1>{this.say}</h1>
  )
}

patch(null, <Hi />, document.body)

Just throwing crazy ideas out there?

mindplay-dk commented 6 years ago

Deferring can prevent some redundant updates - that's good.

But you're betting on setTimeout() as a means of deferring updates until hopefully the element is back in a consistent state - if all updates are performed in a blocking manner within the main thread, that'll probably work most of the time, but... you're accepting partial state updates, which means you can still get into a temporarily incoherent state - if you validate the state before you render, and throw an error, the stack trace won't reveal the source of the bad state, so it's not really a good work-around.

It's also not a very interesting example, because count and title can't really have conflicting values, so there isn't really a case for incoherent state here.

Simplest example I can think of: a star rating widget with a score and max property - it displays max stars, score of them highlighted, the rest grayed out.

Case: if max and state are both currently 5, and you set score to 10 before updating max to 10 - the state will be temporarily 10 out of 5 stars, which should fail.

The real point though, is that if max is currently 5, in a React component, I would throw immediately for setState({ score: 10 }), which would be an actual error - while updating atomically with setState({ score: 10, max: 10}) is guaranteed to always work regardless of current state.

Presumably, you do see the problem now, right? :-)

My main point is I'm not entirely happy with the internal architecture of custom elements. I know we can mostly work around the ugly and achieve something that works approximately the way we want, under most normal conditions - and as said, I also do understand that it has to work like this, or else they'd have had to change the DOM API itself...

jorgebucaran commented 6 years ago

No, I don't see the problem (yet). I find custom elements an adequate alternative to encapsulating what you call control/accidental state that doesn't interfere with state-first/single-state application design.

Here is a star rating widget with a score and max attributes just like you described. I'm using radio buttons. I was too lazy to improve the CSS at this time.

There were no incoherent states in the making of this pen.

/cc @zaceno @SkaterDad @rbiggs @mindplay-dk

jorgebucaran commented 6 years ago

@mindplay-dk But you're betting on setTimeout() as a means of deferring updates...

We can use an immediately deferred Promise as an alternative to setTimeout like in HAV2, so much room to improve.

mindplay-dk commented 6 years ago

Here is a star rating widget with a score and max attributes just like you described

I mean, yeah, you can just ignore incoherent state like score = 8, max = 5 and render it like score = 5 - my point is mainly that no one would do that in a component; you'd throw errors for invalid state. I'd expect any functional or stateful component to throw an error.

So this is a good example to contrast the difference between custom DOM elements and components:

If all components were implemented as custom elements, well...

Design constraints on elements are much more lax - a well-behaved element, when it's in an incoherent state, literally just has to render some kind of representation, and exhibit some kind of behavior.

Design constraints on software units (components) are generally much stricter, since otherwise you'd get into debugging hell on every project quite quickly: you'd never know when, why or where a bug was introduced - literally you'd have to click through your entire UI and visually test all it's constraints, or write elaborate headless browser tests to see if every component instance behaves well, in every case/context, under all possible conditions, because it'd never throw errors.

Bottom line: I wouldn't want to work with custom elements exclusively in large-scale applications.

Components, on the other hand, can validate and report errors, and work very well at scale.

I'm not saying you couldn't come up with some kind of custom element/component hybrid that does report errors, but again, your stack-traces aren't going to help much, since you won't know about errors until after the fact, when it renders - and it's not how DOM elements are supposed to work.

jorgebucaran commented 6 years ago

@mindplay-dk We can do whatever we want, make it throw errors or not, clean the stack traces, make it trigger error events, and so on. You gave me a problem and in a few hours over a few days, I put together some code to show you custom elements can be used as an escape hatch from the strictness of the state-first/single-state purely functional components paradigm in Superfine/Hyperapp.

I think we're done here! 👋