Hacker News new | ask | show | jobs
by danielvaughn 1957 days ago
You could conceive of a GUI as merely a projection of internal state. If you do, then a GUI could be a completely stateless layer that exists solely to render visual content parametrically.

I've been developing a convention in React over the last year that uses this idea and it's very very nice. I'm also trying to write a platform-agnostic UI language specifically for designers that makes this a first class concept.

8 comments

You're just redefining "GUI" as "the MVC View layer". It's good to make the View a pure projection - this is what React approximates - but the state doesn't stop being a concern of the GUI, it just gets pushed to other places in the system (the internal state of HTML elements, the app's store whether that's in Redux or otherwise, even the server if we're talking about a server-rendered app (I would actually argue the main benefit of a client-rendered app is that it's much less onerous to add GUI state; of course the flip-side is that you end up with a lot more GUI state because it's so easy to add))
While I agree with that to a point, I'd like to point out that the post you're replying to also just describes "refactoring state away" (like what you alluded to in your original post.

So, I think that you're both not wrong: State tends to be one of the (if not the) fundamental problems in most of programming. Often, looking at things from another perspective is very helpful. And we should be uncovering new strategies to do that for all places where we haven't, including the GUI.

Of course, that also means that we arrive at the meta-problem of synchronizing the state backing various systems at one point. Which mostly encapsulates Karlton's "two hard things": Cache invalidation and naming things. :)

From my original post (emphasis added):

> The fundamental challenge of GUIs is that they have state as a core concern. Unlike most systems, state is not an implementation detail that can be refactored away.

I do talk about "unifying state"; eliminating "accidental" state. But any UI that allows for interaction also has essential state which can't be refactored away.

> You could conceive of a GUI as merely a projection of internal state.

That mental model has been the most natural for me as well. It really clicked for me when reading Trygve Reenskaug's descriptions of MVC [1], particularly this paragraph (emphasis mine) and the illustration that follows:

> The essential purpose of MVC is to bridge the gap between the human user's mental model and the digital model that exists in the computer. The ideal MVC solution supports the user illusion of seeing and manipulating the domain information directly. The structure is useful if the user needs to see the same model element simultaneously in different contexts and/or from different viewpoints.

[1]: https://folk.universitetetioslo.no/trygver/themes/mvc/mvc-in...

What you describe is the model and view part of the MVC pattern: The view is just a projection of the model.

How does the UI get notified of changes? (like the article discusses some changes might come from different part of the UI, or might even come from external like in a chat client)

How do you handle actions of the user? (Of course in the controller in MVC, but how does it work exactly?)

Thanks for pointing that out... Everything old is new again.

Note that in this case, most modern framework actually focus on what Microsoft called MVVM in Silverlight (Model View View-Model), a lesser strict "clean controller" approach where view and controller are tangled in each other since it's throw away code for the most part, while the model stays clean.

Unless you rewrite the content of every input and textarea and reposition the cursor at every input event, or monitor which element has focus at any given time, you have some DOM components with an internal state.
Which... you mostly can do that with React.

Focus is the hardest one, because that can move around really erratically. But even that sometimes has to be done in state. IE11 doesn’t have descendant focus selectors so at my last job I wrote a hook for our dropdown children to dump their focus state into a React context so our renderers could stay pure and not rely on CSS and therefore DOM state.

Just last week I implemented a hook to reposition a tooltip based on the mouse cursor.

You do need to think about DOM state a little when doing these things. But I would argue that’s somewhat separate from the activity of building a React UI. It’s pretty rare you can’t just render purely based on the React state, using off-the-shelf hooks.

I have built a Qt app using this approach. I created a vdom for Qt to allow for the view to be a pure function of state. However, the app also has some extensive animation, and it runs on a slow embedded processor. I needed an escape hatch to not recompute the entire view on every animation frame on some screens. I think using something like MobX computed expressions could provide the necessary performance to avoid needing the escape hatch. However, the code base is now large and dynamically typed and extensive change is now hard. In general this approach involves recomputing the entire UI and diffing against the previous UI on every state change or maybe every 33ms. If the above takes too long, then it’s a problem.
In cases where you're working with primitives/immutable values (constant-time comparable for change detection), you can use plain-old memoization to avoid redundant work. If you're familiar with React, this is basically what PureComponent tries to do. The limitation, of course, is that this doesn't work with arbitrarily-deep, arbitrarily-mutable state objects. That's where really MobX shines.
That's essentially declarative programming for GUIs, like, say, Flutter does. You have some state provider, and the GUI is fully re-rendered when it is notified of a state change. There's many different actual implementations of this paradigm, but that's it in a nutshell. I've been having lots of fun learning this, coming from a more traditional MVC pattern in Qt and the likes.

See this: https://flutter.dev/docs/get-started/flutter-for/declarative

>> You could conceive of a GUI as merely a projection of internal state.

Absolutely. The error is in trying to treat GUI elements as an actual data model. That and premature optimization trying to only update things as they change.

Once you take that approach the only remaining hard part is that for complex applications, events can trigger state dependent behavior. But that should be at a layer under the GUI toolkit.

So Redux.