DEV Community

Discussion on: What happened to Components being just a visual thing?

redbar0n profile image
Magne Author • Edited on

I agree.

My criticism of React was that it doesn't necessarily follow that the communication pathways and interrelations for the various parts of the application will be accommodated by that same tree.

Indeed. A UI does not behave as a tree. This is vaguely related, and desparingly funny: xkcd.com/2044/

That tree is not necessarily how the application logic needs/wants to be organized.

I think XState is an interesting alternative to organizing application logic (making behavior declarative). Especially on the front end (though potentially also on the back-end) With React: Possibly to the point of avoiding the props interface altogether for updating state (in favor of only using XState's hook) ...

I even think strictly updating CSS shouldn't be done through props, but rather through the CSSOM (like the CSS-in-JS library Stitches does), to avoid triggering prop changes in (and re-rendering of) the entire React tree.

Since the DOM (and VDOM) should be for markup:

HTML markup is transformed into a Document Object Model (DOM); CSS markup is transformed into a CSS Object Model (CSSOM). DOM and CSSOM are independent data structures. -- constructing-the-object-model

But I digress.

By declaring itself as the "V in MVC" React didn't actually address any of the problems but instead positioned itself to absorb functionality from the other two aspects — for better or worse.

I definitely agree with this! Components have become ViewControllers, like in Swift. When Components even contain application state, they become ModelViewControllers...

Which, ironically, isn't so far from what Reenskaug envisioned for MVC (as opposed to the layered architecture it became):

"Every little widget on the screen had its own Model, Controller and View." at MVC is not an Architecture.

Although this approach has some drawbacks, especially related to syncing state changes, as noted at the section starting with "Sometimes MVC is applied at the individual widget level ..." in this excellent and visual MVC explainer article.

rendezvous looks interesting. Although I'm not sure I fully grok it...

This allows client side rendering to be split into two distinct and decoupled phases:

  1. render template (UI structure)
  2. bind behaviour (UI behaviour)

The separation between the "render template" and the "bind behavior" reminds me of StimulusJS. But maybe the thing with rendezvous is that it renders HTML in the same way on the server as on the client? Instead of just manipulating HTML on the client, like Stimulus does.

It also starts with the server and then moves to the client
...
On the client side the state initialization data (to support context) and the transfer messages are extracted from the markup and prepared for use as part of the page configuration. Then the binder definitions are loaded to bind application behaviour to the existing (and future) DOM tree.

I am reminded of Qwik with its resumability vs. replayability. How would they compare?

Thread Thread
peerreynders profile image
peerreynders

FYI:

The liabilities of MVC are as follow:

Intimate connection between view and controller. Controller and view are separate but closely-related components, which hinders their individual reuse. It is unlikely that a view would be used without its controller, or vice-versa, with exception of read-only views that share a controller that ignores all input.

Buschmann, Frank et al. "Pattern-Oriented Software Architecture: A System of Patterns Volume 1". Model-View-Controller, p.142, 1996.

The second division, the separation of view and controller, is less important. Indeed the irony is that almost every version of Smalltalk didn't actually make a view/controller separation. The classic example of why you'd want to separate them is to support editable and noneditable behavior, which you can do with one view and two controllers for the two cases, where controllers are strategies [Gang of Four] for the view. In practice most systems have only one controller per view, however, so this separation is usually not done. It has come back into vogue with Web interfaces where it becomes useful for separating the controller and view again.

The fact that most GUI frameworks combine view and controller has led to many misquotations of MVC. The model and the view are obvious, but where's the controller? The common idea is that it sits between the model and view, as in the Application Controller (379) — it doesn't help that the word "controller" is used in both contexts. Whatever the merits of the Application Controller (379), it's a very different beast from an MVC controller.

Fowler, Martin. "Patterns of Enterprise Application Architecture", Model View Controller, pp 331-332, 2003.

Thread Thread
peerreynders profile image
peerreynders • Edited on

I think XState is an interesting alternative to organizing application logic (making behavior declarative).

XState is a tool — or as I like to say product — the general skill behind the product is statecharts.

My personal attitude

  • General skills tend to depreciate slowly so they are typically a valuable investment.
  • Product skills depreciate quickly so they are the "cost of doing business" and should be minimized.

XState is valuable because it enables the application of statecharts. But as wide as an applicability statecharts may have, they don't solve every problem and for many problems they are overkill especially when yet-another-library has to be pulled in.

Also don't pursue declarative for it's own sake. When it comes to the web HTML and CSS are declarative by default but JavaScript is not. Creating declarative abstractions in JavaScript always comes at a cost — sometimes it's worth it, sometimes it's not — as always it depends.

I even think strictly updating CSS shouldn't be done through props

Why change CSS at runtime at all? CSS rules are designed to remain static for the lifetime of the page but they only become active when the specified conditions are met. You wanted "declarative" but now when you have it you want to imperatively change it at run time?

Perhaps what is needed is tooling that can weave together the required CSS style sheet at design time — prior to deployment.

Components have become ViewControllers

As I sourced in my other reply, View and Controller have traditionally always been heavily coupled in practice so the V-C separation really only existed in idealized descriptions. As you remark, the real problem starts when application state gets pulled into a UI component.

Which, ironically, isn't so far from what Reenskaug envisioned for MVC

Robert C. Martin's account may not perfectly align with Trygve Reenskaug.
(trygve also happens to be a DCI programming language)


In Smalltalk-76, the forerunner to Smalltalk-80, the idea was to let objects represent some information of interest to the user and also to know how to present this information on the screen and let the user edit it. This very powerful paradigm is the basis of the intuitively pleasing object-oriented user interfaces so popular today.

This concept proved inadequate when I wanted to use Smalltalk-76 to create a system for production control in shipbuilding. The information represented in the system was the production schedule with its activities and resources, and the user would want to see and manipulate it in many different forms: as a network of activities, as a chart showing each activity as a bar along the time axis, and as a business form presenting activity attributes as texts that could be edited.

A natural consequence of this was to tear the original object apart, so that one object represents the information, one is responsible for the presentation and one for capturing input from the user. The first was called the model object, the second was called the view object and the third was called the controller object. This gave the freedom to have many different presentations and input facilities for the same object, and even to have several views of a given model on the screen simultaneously.

The object-oriented, direct manipulation user interface gives the user an illusion of working directly with the apparently concrete information objects. The Model-View-Controller breaks this illusion when the user has several views on the same information object simultaneously. This is fortunately of no concern to the professional planner who is manipulating different views of the same plan even in the manual systems.

Reenskaug, Trygve et al. "Working with objects The OOram Software Engineering Method", 9.3.2 Model-View-Controller, pp.333-334, 1995.


So given that models were shared among several views it makes no sense for the model to be inside a single UI component (view + controller). MVVM introduced the idea of a View Model which could be co-located with the View.

But maybe the thing with rendezvous is that it renders HTML in the same way on the server as on the client?

I've only looked at Stimulus JS briefly. In terms of my write up I would say it focuses only on the variation (client side functionality) because in Rails-land there is no commonality to exploit given that none of the server side code — not even any language-independent templates — can be reused on the client side. So when it comes to markup you have to write the template logic twice (I'm not aware of any tooling that translates a common template spec to server and client code).

The other thing is whenever I look at it, it's so infuriatingly Rails. It makes sense because that is the ecosystem it was created for but at same time it seemed to assert its Rails-ness even at the cost of being less web-ish. In my opinion Andrea Giammarchi's libraries are "of the web" — his work tends to be in alignment with "the platform" even when he doesn't agree with it and does he ever know how to get the most out of it. Meanwhile Rails just wants to be Rails regardless of what the web is doing.

Some of my pet peeves with Stimulus JS:

  • Any instance specific information has to stuffed into the markup. And as Harry Roberts points out:

A common practice is to use data-* attributes as JS hooks, but this is incorrect. data-* attributes, as per the spec, are used to store custom data private to the page or application (emphasis mine). data-* attributes are designed to store data, not be bound to.

data-controller identifies the controller category (class) but not the controller instance, while other data-* conventions litter the markup with "quasi-bindings" (that have nothing to do with the markup's responsibility of structuring content) for the sake of being "declarative". The cost of being declarative is having to inherit from the Controller class which presumably scans the markup at runtime for the data-* binding sites which uncomfortably reminds me of Vue's DOM templates. The Controller is heavily coupled to the markup — how much work is it really to correctly bind with the DOM with a few imperative steps right in the connect instead of the Controller base class doing who knows what.

  • All the controller files are stored under one single controllers folder. Really? I don't put all my cutlery, paring, chef's knives and box cutters in the same drawer just because they are all "knives". Why should I organize my code that way?

Rendevous in essence hijacks custom element-like ideas and mechanisms to late-bind client side behaviour to the DOM — regardless of whether that DOM is the result of parsed server HTML or client side rendering (and either are created with the same template or template function).

How would they compare?

Don't know, haven't looked at it in detail. For the time being from what I have heard Astro sounds worth investigating — the said, the current astro/qwik/remix hype is giving me visions of this - making me wonder if they've gone a bridge too far.

Thread Thread
redbar0n profile image
Magne Author

I agree with your attitude and advice, re: XState and statecharts.

Why change CSS at runtime at all?

To change the theme of the page. Dark mode, for instance.

CSS rules are designed to remain static for the lifetime of the page but they only become active when the specified conditions are met. You wanted "declarative" but now when you have it you want to imperatively change it at run time?

Perhaps what is needed is tooling that can weave together the required CSS style sheet at design time — prior to deployment.

Yes. Stitches.dev is that tool. It allows a declarative way of writing CSS-in-JS at design time, while also weaving it together and changing it for you at run time.

Some of my pet peeves with Stimulus JS:

Thanks for sharing! It's very interesting to read your insights. I wouldn't have thought of that myself. Personally, my issue with Stimulus is just that I fear I'll incorrectly wire things up due to typos. Plus that with all the data-* prefixes, it's visually confusing (I feel that data-hello-target should have been a simple id). So I can't really quickly scan it to get the info I need. The latter is an issue I have with Ionic Framework too, with it's insistence on prefixing all their components with ion-* (when it ought to have been a suffix, imho).

the current astro/qwik/remix hype is giving me visions of this - making me wonder if they've gone a bridge too far.

I agree. I am overwhelmed by just trying to understand and differentiate all the different approaches...

I've suggested to Ryan Carniato that he - or someone equally knowledgeable - should make a standardised comparison diagram of the different approaches we have today:
twitter.com/magnemg/status/1435235...
twitter.com/magnemg/status/1435280...
twitter.com/magnemg/status/1438271...

Thread Thread
peerreynders profile image
peerreynders

To change the theme of the page. Dark mode, for instance.

Perhaps I'm naive but I would have thought that's a job for CSS custom properties i.e. an imperative swap of the design tokens that relate to light/dark.

I wouldn't have thought of that myself.

Don't get me wrong. Rails had a point to employ "convention over configuration" to counter the J2EE XML configuration madness at the time. But if you apply "convention over configuration" dogmatically you end up with an awful lot of convention that you have to keep in your head. It's always a question of balance; hard coding vs configuring vs convention and it's not easy to hit the optimum (which depends on the circumstances).

The biggest problem with the rendezvous example is that static.js — for a full page that would be a monster to work with. So you really would need tooling to map a more developer friendly representation to the machine friendly representation.

I fear I'll incorrectly wire things up due to typos. Plus that with all the data-* prefixes,

Look I completely empathize — I immediately thought: "I wish there was a tool that could verify that all the bindings are set up correctly".

Going back even further, when learning JavaScript I found myself craving the false sense of security that a C#/Java compiler gave me that I at least I got the syntax right. At the time I would have been very receptive to TypeScript (these days — poor ROI — how things change).

Now please indulge a digression here — I'll get to a point soon enough.


Oliver Steele: The IDE Divide

That article differentiates between the

  • Language-Maven
  • Tool-Maven

Given that presumed dichotomy in developer attitudes and my personal reading of the declarations of the vocal minority on social media I'm lead to the conclusion that the currently active generation of developers is immensely skewed towards the "I'll adopt it when the tooling is better" type.

That means that a "good idea" has to survive to the early/late majority stage of the innovation adoption lifecycle.

Innovation Adoption Lifecycle

That makes me wonder: How many "good ideas" die on the "innovators" and "early adopters" hill because the developer population is skewed so much towards the "Tool-Mavens" rather than the "Language-Mavens"?


So if you can make Stimulus JS work then by all means do it!

  • Now in your situation you'll be committed to formulating your templates twice and you'll have to keep them in sync — that's just how it is. However I'd consider adopting something like µhtml for client side templating for the larger partials and I would wrap each template in a (pure) function, passing it all the data it needs to instantiate.

  • Whenever possible use reactive bindings to update values inside the DOM rather than replacing nodes. This is the reason why I wish there was a non-UI core of Solid — for most purposes MobX seems a bit too heavy — I'd like something that is lighter than Redux. I guess one could always roll your own.

  • Keep those (Stimulus JS) controllers as skinny as possible. Their job is fairly narrow:

    • On connect/mount
      • Wire reactive subscriptions for value updates into the DOM and add event listeners for input.
      • If the nested content isn't already present use templates to create it client side (their controller(s) will take care of the rest).
    • On disconnect/unmount
      • Remove event listeners and unsubscribe the reactive bindings.
    • Whatever you do keep actual page application logic out of the (Stimulus JS) controllers.

Remember the aim (of rendezvous) is to have a JavaScript core application that can be effectively micro-tested without:

  • running it in a browser
  • having to spin up a fake DOM.

The templates, controller, event and reactive bindings are tested during integration testing with something like cypress or perhaps Puppeteer. The key is that by this point in time you already have full confidence in your decoupled, micro-tested page application logic.

when it ought to have been a suffix, imho

You have to keep in mind that while most browsers will handle non-standard attributes those attributes will actually invalidate the HTML (which is why those pesky alpine.js x-* attributes are another pet peeve of mine). If you are going to 'invent' attributes on standard HTML elements then they better be data-* attributes. At least Stimulus JS got that right.