I've used several MVC UI frameworks in the past both on the desktop and web, and they all had the same problems. When the view becomes out of sync with the model, it becomes a nightmare to figure out why that's happening.
You have your model (data), and conceptually, you just want to bind that model to the view. But you have to write all this imperative code to do that. It's very simple with an imperative UI to have infinite loops even with very simple UIs. "When this field changes, I want to update this other field". But that other field has it's own event listeners that trigger other effects. The event listeners work fine in their own silo, but the interactions between fields cause unintended effects that are not obvious reading the code.
When the component is mounted, the behavior is very hard to reason about and you spend lots of time in a debugger tracing through all the event listeners just to reach the initial state of the UI. Additionally, in these frameworks the developer is typically responsible for cleaning up all the event listeners. It's very easy to forget to clean these up properly.
Once a UI reaches a certain complexity, you try to rewrite the imperative code so that it almost reads declarative and you wish you could simply use a declarative UI framework. I can't speak for React as I have not used that in a long time, but in Vue.js I never write the UI to use global state. All state is local to the views that it pertains to and with defineModel introduced back in late 2023, you can write small UI components without all the plumbing required in earlier versions. I've never experienced such a simple and reliable UI framework previously.
> "When this field changes, I want to update this other field".
That's the problem MVC solves, and that not just a vast majority of so-called MVC frameworks (that are nothing of the sort) get fundamentally wrong, but also a lot of people talking about MVC, in particular those who want to "fix" this non-problem of MVC (see: React).
In MVC, fields never update other fields. The model also doesn't update any fields.
1. UI events update the model, and only the model
2. The model notifies the UI that it has been updated, and the UI is now inconsistent. It does not initiate an actual update, nor does send data to the UI.
3. The UI fixes the inconsistency by updating itself from the model. This can be a full update, or it can do something more clever to only update parts it knows have changed. This may be in response to an update notification or not.
No update loops. No inconsistencies (well except the temporary ones until the UI has updated itself, but those are part of the deal). Solved in the 70s.
I wrote about this here: Model Widget Controller (MWC) aka: Apple "MVC" is not MVC
The update loop occurs as a result of step 3 in which the UI updates itself from the model which then, due to a combination of developer inexperience, UI framework design/complexity, or application complexity, the UI then automatically triggers updates to the model. In my experience, this was a common defect in all applications I've seen that used various MVC style frameworks or home grown MVC architectures.
In a declarative framework, while it is still technically possible, it is very rare. The current app I'm working on is one of the largest front-end apps I've worked on (multiple years of dev). We've had a single infinite loop issue reported that was trivial to fix. We have very junior UI devs compared to teams I've worked on in the past, yet our UI defect rate is incredibly low.
> The update loop occurs as a result of step 3 in which the UI updates itself from the model which then, due to a combination of developer inexperience, UI framework design/complexity, or application complexity, the UI then automatically triggers updates to the model.
In an actual MVC framework, that can't actually happen.
> It's very simple with an imperative UI to have infinite loops even with very simple UIs.
That's something that reactive UIs inherited from the imperative events by design. It's not a differentiator here. You may be able to debug them better, but infinite event streams are pretty much still an issue, and about as easy to get.
Anyway, almost every problem in the article is a react problem, not a FRP problem. Vue may not have them, I don't know it well enough to say (but some do really look like a FRP in javascript problem, so they should apply there too).
> When I first saw React.js, I had a quick glance and thought that it was cool, they finally figured out how to do a Cocoa-like MVC UI framework in JavaScript.
> Overall, though, the use of a true MVC UI framework seemed like a great step forward
The author keeps saying 'MVC' as if it ever made any sense in the context of React. The old react docs used to quip that react was the v of mvc, until this notion was scraped entirely; and although one might perhaps suggest that props and templating is a v, and state is conceivably an m, there has never been anything concrete one could point out and argue that it is a c.
> The old react docs used to quip that react was the v of mvc, until this notion was scraped entirely
I'm still treating React as the "V of MVC", other things handled by other things, and seems I'm not alone with this.
I think at one point React got so popular, and the team grew, so they figured they need to expand the scope for it to continue growing, and Facebook continued to throw resources at it, and as we know, things that don't change are clearly dead (sarcasm), so APIs changed, the scope grew and eventually they stopped marketing it as the view-layer, and instead push it as a full thing, when it still clearly was just a view library, not a framework.
I am also using React as the "V in MVC". It is the most sensible way to use React. I also only use class components. My code is way simpler than React code these days. The only React concepts I need are props, state, and the lifecycle methods.
React was originally designed to be the "V in MVC". What happened then is the community took it in a different direction: specifically ReactRouter.
It's really a company/team culture thing. Either you end up grouped together with people who always chase the latest fads and cargocult their way to their promotions, or you end up with the "Justify everything" camp where anything new is shit on.
Ideally, you end up in a company/team that sits somewhere in the middle, faithfully evaluating the trade-offs of everything, and don't spend 60% of their time reading the latest news from blogs and social media.
Right. Anything other than the most popular way of doing things is "wrong" to them. That's how mistakes get established. This non-questioning attitude seems especially true among frontend developers.
> The author keeps saying 'MVC' as if it ever made any sense in the context of React.
1. That part is clearly about my first superficial impression and how I later discovered those impressions were...let's say "insufficient", at least in the context of how React is now presented.
2. I later listened to an interview with Pete Hunt, one of the original creators on The Future of Coding
I was once again surprised, this time pleasantly, that building an MVC-style framework like the desktop ones (Cocoa, Win-flavor-of-the-month, etc.) was exactly what they were aiming for.
Their implementation of those concepts was different, but it was what they were aiming for. I don't know what exactly happened, but it is a common occurrence that adopters of a technology are much more religious about it than the initial creators, who tend to be more pragmatic.
And of course, the humorous point that MVC solves exactly the supposed problems of MVC that React is trying (and trying, and trying, and trying...) to solve just never gets old.
>The old react docs used to quip that react was the v of mvc
I remember this! Vue was meant to be the same thing (I suspect Vue's name comes from "view"). It was meant to be super light and then they started adding more and more non-core tools until they went full Angular. Which is ironic as Angular's full on batteries was always used against it
It is funny that MVC as a concept started as a backend server-rendered pages where the V was what rendered the HTML, C was the business logic which pulled data from the M which was the database + adapters.
When bending it to apply to React + JSON-apis it kinda applies, but it is such a different approach that conceptually it is not relevant in the presence of a lot of client state.
"Trygve Reenskaug created MVC while working on Smalltalk-79 as a visiting scientist at the Xerox Palo Alto Research Center (PARC) in the late 1970s. He wanted a pattern that could be used to structure any program where users interact with a large, convoluted data set. His design initially had four parts: Model, view, thing, and editor. After discussing it with the other Smalltalk developers, he and the rest of the group settled on model, view, and controller instead."
A model is supposed to be a domain model, the business logic of the app, not simply a database access layer. Your logic should reside in the model layer, sort of like building a library of classes and functions that you can reuse. That's why in the server-side world people use the term "fat models" to describe this type of use.
> When bending it to apply to React + JSON-apis it kinda applies
There is no bending, the original MVC applied to separating the business logic from rendering views and capturing input events, because this separation promotes code reuse. You can write many different views and controllers in your UI that show and react to user's input but still talk to the same underlying logical model.
I like this github repo to understand how MVC applies to programs with graphical interfaces. It's JS and HTML, so it's easy to follow.
> It is funny that MVC as a concept started as a backend server-rendered pages where the V was what rendered the HTML, C was the business logic which pulled data from the M which was the database + adapters.
I've got a CD from 1995 for Watcom C/C++. I purchased this compiler and IDE for a large (for me) sum of money and used it to create many Win32 applications.
I recall that that the help pages (which were extensive) came along with some non-reference documentation, which gave a quick overview of MVC for writing Win3.1 and Win95 applications, so MVC was already well-known in GUI systems even back then.
React is not pure at all, and has a few gotchas related to updates. That being said, the JSX and composability of it is unmatched and I think the main reason React "won".
I think a logical continuation of this model is something like Solid.js, where there is no concept of re-rendering, just atomic DOM updates when observables change their values, but somehow this approach didn't get critical traction.
Nothing of value is 100% "pure", eventually you are gonna need to draw something to the screen, so trying to reach that sort of utopia seems futile.
With that said, the "mental model" of React, or maybe the focus, is "pure" where it matters, namely that you have data, pass it through a function, and you get a view/UI, and if you pass the same data, you get the same UI, regardless of what happened before.
When it appeared, was very different from us web developers were used to, where sometimes the DOM even was our data store, and mutation was the name of the game.
So when people say "React is pure", I don't think they're talking about internally, or "100% pure", just that the mental model you need to adjust to is that the UI gets created from (mostly) pure functions.
Of course, this all breaks down once the frontend application actually runs in a browser, but that doesn't mean it isn't valuable for the developer as things gets a lot simpler.
Not disagreeing with you in the end I guess, just clarifying for those who might not be familiar with React, and get confused when some people say it's pure, while others don't. Both are right :)
Nope, that is precisely what pure functional programming is about: to turn actions like "draw something to the screen" into regular values that you can store into a variable, pass around, return from a function and so on.
It's not an utopia. It will eventually happen and it will replace how react.js currently works. effect.website will probably be the foundation.
I'm well aware of what "pure functional programming" is about, I spend most of my time in Clojure during a normal work day, and done my fair deal of Haskell too :)
And yes, even the most pure functional language eventually needs to do something non-pure, even if the entire flow up until that point is pure, that last step (IO) just cannot be pure, no matter how badly you want it to.
With that said, you'd have to pry my pure functions out of my cold dead hands, but I'm not living under the illusion that every function can be pure in a typical application, unless you have no outputs at all.
> I think a logical continuation of this model is something like Solid.js, where there is no concept of re-rendering, just atomic DOM updates when observables change their values,
Knockout js worked something like that. There are complexities with the observable approach though, especially around batched updates and avoiding update loops.
Not to mention debugging Knockout.js applications was as complex as debugging jQuery applications, mutable state that can be changed from anywhere and from any direction makes it really hard to see what's going on. React was a breadth of fresh air when it appeared.
You could use angular. Pretty sure it even supports SSR and hydration, signals and a lot of other random stuff these days. Personally I much prefer using functions though.
In Elm the UI is 100% pure, and they are super adamant that local state is evil and a mistake. They sort of get away with it because there is already hidden state like input focus/selection, or scroll positions. But it's pretty clear that it's not practical to do full purity without local state in reality.
With lenses its possible to create component interfaces that make local and global state interchangeable. Even for focus, selection, scrollposition components use local state by default but that can replaced by a lense into a global state.
This make stuff like synching two scroll positions or selectiom ranges super easy and allows for consistent manual focus management across components.
But what’s the benefit of this approach? It seems needlessly expensive, both in terms of computational overhead (walking up and down the state tree) and how much more code it requires.
I guess you gain persistence of your entire app’s state (which makes time travel debugging easier) but that’s all I can think of.
In my experience when applying this architecture to react or svelte, the performance is quiet good, often better than classic alternative approaches (redux, many local useState)
But the main benefit is not only the persistence of the app state, but composable globally consistent state.
With most state management solutions at some point you hit a wall where communicating state changes between far away components is really difficult. You need to manually manage eventlistener lifecycles, route events through the application and convert events into state changes and state changes into events, while not causing cycles or unstable feedback loops or race conditions.
With lenses the state tree is composed declaratively and locally. each component does not need to care if the parent state is locally constructed or coming from further away.
/edit:
Actually using this approach you are not forced to have a single global state but instead each component can simply decide to embed its child components state into its own state or not.
If all components decide to embed their children state you get a single global state. But if all components decide to not embed their children state you get only local state with no communication. But the unified Interface gives you the choice.
This is very detailed introduction and documetation of a lense-based architecture in react. [2]
At the first glance it looks kind of overwhelming but it contains lots of examples.
This is a case study of many rather complex components. [3] The codinsteade is rather quick and dirty hacked together and at the first glance not really readable but thats kind of the point: even complex components can simply be composed locally from many small parts of requiring some global controller managing the state and keeping it consistent.
I've used several MVC UI frameworks in the past both on the desktop and web, and they all had the same problems. When the view becomes out of sync with the model, it becomes a nightmare to figure out why that's happening.
You have your model (data), and conceptually, you just want to bind that model to the view. But you have to write all this imperative code to do that. It's very simple with an imperative UI to have infinite loops even with very simple UIs. "When this field changes, I want to update this other field". But that other field has it's own event listeners that trigger other effects. The event listeners work fine in their own silo, but the interactions between fields cause unintended effects that are not obvious reading the code.
When the component is mounted, the behavior is very hard to reason about and you spend lots of time in a debugger tracing through all the event listeners just to reach the initial state of the UI. Additionally, in these frameworks the developer is typically responsible for cleaning up all the event listeners. It's very easy to forget to clean these up properly.
Once a UI reaches a certain complexity, you try to rewrite the imperative code so that it almost reads declarative and you wish you could simply use a declarative UI framework. I can't speak for React as I have not used that in a long time, but in Vue.js I never write the UI to use global state. All state is local to the views that it pertains to and with defineModel introduced back in late 2023, you can write small UI components without all the plumbing required in earlier versions. I've never experienced such a simple and reliable UI framework previously.
Author here.
> "When this field changes, I want to update this other field".
That's the problem MVC solves, and that not just a vast majority of so-called MVC frameworks (that are nothing of the sort) get fundamentally wrong, but also a lot of people talking about MVC, in particular those who want to "fix" this non-problem of MVC (see: React).
In MVC, fields never update other fields. The model also doesn't update any fields.
1. UI events update the model, and only the model
2. The model notifies the UI that it has been updated, and the UI is now inconsistent. It does not initiate an actual update, nor does send data to the UI.
3. The UI fixes the inconsistency by updating itself from the model. This can be a full update, or it can do something more clever to only update parts it knows have changed. This may be in response to an update notification or not.
No update loops. No inconsistencies (well except the temporary ones until the UI has updated itself, but those are part of the deal). Solved in the 70s.
I wrote about this here: Model Widget Controller (MWC) aka: Apple "MVC" is not MVC
https://blog.metaobject.com/2015/04/model-widget-controller-...
And here: Blackbird: A reference architecture for local-first connected mobile apps
https://blog.metaobject.com/2022/06/blackbird-simple-referen...
The update loop occurs as a result of step 3 in which the UI updates itself from the model which then, due to a combination of developer inexperience, UI framework design/complexity, or application complexity, the UI then automatically triggers updates to the model. In my experience, this was a common defect in all applications I've seen that used various MVC style frameworks or home grown MVC architectures.
In a declarative framework, while it is still technically possible, it is very rare. The current app I'm working on is one of the largest front-end apps I've worked on (multiple years of dev). We've had a single infinite loop issue reported that was trivial to fix. We have very junior UI devs compared to teams I've worked on in the past, yet our UI defect rate is incredibly low.
> The update loop occurs as a result of step 3 in which the UI updates itself from the model which then, due to a combination of developer inexperience, UI framework design/complexity, or application complexity, the UI then automatically triggers updates to the model.
In an actual MVC framework, that can't actually happen.
> It's very simple with an imperative UI to have infinite loops even with very simple UIs.
That's something that reactive UIs inherited from the imperative events by design. It's not a differentiator here. You may be able to debug them better, but infinite event streams are pretty much still an issue, and about as easy to get.
Anyway, almost every problem in the article is a react problem, not a FRP problem. Vue may not have them, I don't know it well enough to say (but some do really look like a FRP in javascript problem, so they should apply there too).
> When I first saw React.js, I had a quick glance and thought that it was cool, they finally figured out how to do a Cocoa-like MVC UI framework in JavaScript.
> Overall, though, the use of a true MVC UI framework seemed like a great step forward
The author keeps saying 'MVC' as if it ever made any sense in the context of React. The old react docs used to quip that react was the v of mvc, until this notion was scraped entirely; and although one might perhaps suggest that props and templating is a v, and state is conceivably an m, there has never been anything concrete one could point out and argue that it is a c.
> The old react docs used to quip that react was the v of mvc, until this notion was scraped entirely
I'm still treating React as the "V of MVC", other things handled by other things, and seems I'm not alone with this.
I think at one point React got so popular, and the team grew, so they figured they need to expand the scope for it to continue growing, and Facebook continued to throw resources at it, and as we know, things that don't change are clearly dead (sarcasm), so APIs changed, the scope grew and eventually they stopped marketing it as the view-layer, and instead push it as a full thing, when it still clearly was just a view library, not a framework.
I am also using React as the "V in MVC". It is the most sensible way to use React. I also only use class components. My code is way simpler than React code these days. The only React concepts I need are props, state, and the lifecycle methods.
React was originally designed to be the "V in MVC". What happened then is the community took it in a different direction: specifically ReactRouter.
But if you advocate for this now, newer engineers think you’re doing it wrong.
It's really a company/team culture thing. Either you end up grouped together with people who always chase the latest fads and cargocult their way to their promotions, or you end up with the "Justify everything" camp where anything new is shit on.
Ideally, you end up in a company/team that sits somewhere in the middle, faithfully evaluating the trade-offs of everything, and don't spend 60% of their time reading the latest news from blogs and social media.
Right. Anything other than the most popular way of doing things is "wrong" to them. That's how mistakes get established. This non-questioning attitude seems especially true among frontend developers.
Influencer marketing is a big part of the problem here.
The best practices engineering influencers shill are geared towards shipping a todo list quickly, and no thought is given towards maintainability.
To be fair, though, through their eyes everything looks like a nail.
Author here:
> The author keeps saying 'MVC' as if it ever made any sense in the context of React.
1. That part is clearly about my first superficial impression and how I later discovered those impressions were...let's say "insufficient", at least in the context of how React is now presented.
2. I later listened to an interview with Pete Hunt, one of the original creators on The Future of Coding
https://web.archive.org/web/20220519023301/https://futureofc...
Highly recommended!
I was once again surprised, this time pleasantly, that building an MVC-style framework like the desktop ones (Cocoa, Win-flavor-of-the-month, etc.) was exactly what they were aiming for.
Their implementation of those concepts was different, but it was what they were aiming for. I don't know what exactly happened, but it is a common occurrence that adopters of a technology are much more religious about it than the initial creators, who tend to be more pragmatic.
And of course, the humorous point that MVC solves exactly the supposed problems of MVC that React is trying (and trying, and trying, and trying...) to solve just never gets old.
>The old react docs used to quip that react was the v of mvc
I remember this! Vue was meant to be the same thing (I suspect Vue's name comes from "view"). It was meant to be super light and then they started adding more and more non-core tools until they went full Angular. Which is ironic as Angular's full on batteries was always used against it
It is funny that MVC as a concept started as a backend server-rendered pages where the V was what rendered the HTML, C was the business logic which pulled data from the M which was the database + adapters.
When bending it to apply to React + JSON-apis it kinda applies, but it is such a different approach that conceptually it is not relevant in the presence of a lot of client state.
It started waaaaay before backend server-rendered pages
https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93con...
"Trygve Reenskaug created MVC while working on Smalltalk-79 as a visiting scientist at the Xerox Palo Alto Research Center (PARC) in the late 1970s. He wanted a pattern that could be used to structure any program where users interact with a large, convoluted data set. His design initially had four parts: Model, view, thing, and editor. After discussing it with the other Smalltalk developers, he and the rest of the group settled on model, view, and controller instead."
The server-based MVC pattern adopted in Rails is not the original one described for use in interfaces, it could be considered a misinterpretation.
https://andrzejonsoftware.blogspot.com/2011/09/rails-is-not-...
https://news.ycombinator.com/item?id=3035549
A model is supposed to be a domain model, the business logic of the app, not simply a database access layer. Your logic should reside in the model layer, sort of like building a library of classes and functions that you can reuse. That's why in the server-side world people use the term "fat models" to describe this type of use.
> When bending it to apply to React + JSON-apis it kinda applies
There is no bending, the original MVC applied to separating the business logic from rendering views and capturing input events, because this separation promotes code reuse. You can write many different views and controllers in your UI that show and react to user's input but still talk to the same underlying logical model.
I like this github repo to understand how MVC applies to programs with graphical interfaces. It's JS and HTML, so it's easy to follow.
https://github.com/madhadron/mvc_for_the_web/tree/master
> It is funny that MVC as a concept started as a backend server-rendered pages where the V was what rendered the HTML, C was the business logic which pulled data from the M which was the database + adapters.
I've got a CD from 1995 for Watcom C/C++. I purchased this compiler and IDE for a large (for me) sum of money and used it to create many Win32 applications.
I recall that that the help pages (which were extensive) came along with some non-reference documentation, which gave a quick overview of MVC for writing Win3.1 and Win95 applications, so MVC was already well-known in GUI systems even back then.
React is not pure at all, and has a few gotchas related to updates. That being said, the JSX and composability of it is unmatched and I think the main reason React "won".
I think a logical continuation of this model is something like Solid.js, where there is no concept of re-rendering, just atomic DOM updates when observables change their values, but somehow this approach didn't get critical traction.
Nothing of value is 100% "pure", eventually you are gonna need to draw something to the screen, so trying to reach that sort of utopia seems futile.
With that said, the "mental model" of React, or maybe the focus, is "pure" where it matters, namely that you have data, pass it through a function, and you get a view/UI, and if you pass the same data, you get the same UI, regardless of what happened before.
When it appeared, was very different from us web developers were used to, where sometimes the DOM even was our data store, and mutation was the name of the game.
So when people say "React is pure", I don't think they're talking about internally, or "100% pure", just that the mental model you need to adjust to is that the UI gets created from (mostly) pure functions.
Of course, this all breaks down once the frontend application actually runs in a browser, but that doesn't mean it isn't valuable for the developer as things gets a lot simpler.
Not disagreeing with you in the end I guess, just clarifying for those who might not be familiar with React, and get confused when some people say it's pure, while others don't. Both are right :)
Nope, that is precisely what pure functional programming is about: to turn actions like "draw something to the screen" into regular values that you can store into a variable, pass around, return from a function and so on.
It's not an utopia. It will eventually happen and it will replace how react.js currently works. effect.website will probably be the foundation.
I'm well aware of what "pure functional programming" is about, I spend most of my time in Clojure during a normal work day, and done my fair deal of Haskell too :)
And yes, even the most pure functional language eventually needs to do something non-pure, even if the entire flow up until that point is pure, that last step (IO) just cannot be pure, no matter how badly you want it to.
With that said, you'd have to pry my pure functions out of my cold dead hands, but I'm not living under the illusion that every function can be pure in a typical application, unless you have no outputs at all.
> React is not pure at all
Yeah, my pull request to make the documentation honest about this has languished.
https://github.com/reactjs/react-basic/pull/12
> I think a logical continuation of this model is something like Solid.js, where there is no concept of re-rendering, just atomic DOM updates when observables change their values,
I believe Vue is going in that direction https://www.vuemastery.com/blog/the-future-of-vue-vapor-mode...
It exists, but as a Haskell library called Reflex. This is why it didn't gain traction ;)
Knockout js worked something like that. There are complexities with the observable approach though, especially around batched updates and avoiding update loops.
Not to mention debugging Knockout.js applications was as complex as debugging jQuery applications, mutable state that can be changed from anywhere and from any direction makes it really hard to see what's going on. React was a breadth of fresh air when it appeared.
Is there a trend of new web framework that try to go back to actually using classes / objects instead of functions to build UI components ?
You could use angular. Pretty sure it even supports SSR and hydration, signals and a lot of other random stuff these days. Personally I much prefer using functions though.
This post is from 2018
depressing
In Elm the UI is 100% pure, and they are super adamant that local state is evil and a mistake. They sort of get away with it because there is already hidden state like input focus/selection, or scroll positions. But it's pretty clear that it's not practical to do full purity without local state in reality.
With lenses its possible to create component interfaces that make local and global state interchangeable. Even for focus, selection, scrollposition components use local state by default but that can replaced by a lense into a global state. This make stuff like synching two scroll positions or selectiom ranges super easy and allows for consistent manual focus management across components.
NB there is no such word as lense. Lens is a word, as is lenses. But the singular form is lens.
But what’s the benefit of this approach? It seems needlessly expensive, both in terms of computational overhead (walking up and down the state tree) and how much more code it requires.
I guess you gain persistence of your entire app’s state (which makes time travel debugging easier) but that’s all I can think of.
In my experience when applying this architecture to react or svelte, the performance is quiet good, often better than classic alternative approaches (redux, many local useState)
But the main benefit is not only the persistence of the app state, but composable globally consistent state. With most state management solutions at some point you hit a wall where communicating state changes between far away components is really difficult. You need to manually manage eventlistener lifecycles, route events through the application and convert events into state changes and state changes into events, while not causing cycles or unstable feedback loops or race conditions.
With lenses the state tree is composed declaratively and locally. each component does not need to care if the parent state is locally constructed or coming from further away.
/edit: Actually using this approach you are not forced to have a single global state but instead each component can simply decide to embed its child components state into its own state or not. If all components decide to embed their children state you get a single global state. But if all components decide to not embed their children state you get only local state with no communication. But the unified Interface gives you the choice.
Can you point to a code sample using lenses?
This is a very simple example in svelte [1]
This is very detailed introduction and documetation of a lense-based architecture in react. [2] At the first glance it looks kind of overwhelming but it contains lots of examples.
This is a case study of many rather complex components. [3] The codinsteade is rather quick and dirty hacked together and at the first glance not really readable but thats kind of the point: even complex components can simply be composed locally from many small parts of requiring some global controller managing the state and keeping it consistent.
1: https://svelte.dev/playground/ea842faa235540bba43d7970ecf8ce...
2: https://github.com/calmm-js/documentation/blob/master/introd...
3. https://github.com/laszlokorte/svatom?tab=readme-ov-file
Elm died however many years ago and it's still a more pleasant experience than a typical React stack.