> We do not make the (unwarranted) assumption that this transformation can or should be expressed as a pure function. While that would be nice, there are many reasons why this is not a good idea, some pretty obvious.
So obvious they are left as an exercise for the reader?
This whole article seems to be based on the idea that pure functional programming is absurd and impossible, and this is just obvious and doesn't need to be shown. For someone who has written a decent amount of Elm, it certainly isn't obvious to me.
The article is explaining those reasons, which the author feels are obvious but he spells out nonetheless. The gist of his argument is that because UI is not a pure function of a little pile of centrally defined state, React has to come up with lots of new concepts and mechanisms to wallpaper over the conceptual gap. Hence his table which by the end is boiling down to "OOP toolkits don't need this" alongside explanations of very complex FP approaches.
Well, in reading it, those reasons didn't seem to follow on from those points to me. They don't really address working in a pure way from state, just specific things they don't like in a particular implementation.
Specifically "UI is not a pure function of a little pile of centrally defined state" is just this assumption that isn't true, Elm literally gives you no choice, you only have that, and it works, so it clearly can be.
It also ignored all the value you gain from doing it that way. As I say, I've written a fair bit of Elm at this point, and it can often be verbose which can be annoying, but it is actively not complex in the best way, with it being very easy to reason about and bulletproof once it compiles.
I should have specified "for UI", I suppose, but the quote I gave, and:
> The idea of UI being a pure function of the model seems so obviously incorrect, and leads to such a plethora of problems, that it is a bit puzzling how one could come up with it in the first place, and certainly how one would stick with it in face of the avalanche of problems that just keeps coming. A part of this is certainly the current unthinking infatuation with functional programming ideas. These ideas are broadly good, but not nearly as widely or universally applicable as some of their more starry-eyed proponents propose (I hesitate to use the word "think" in this context).
> A UI is NOT simply a replication of server / business logic state.
I don't think that was ever the claim, the rest of the article is similarly misguided. UI as a function of state doesn't mean "UI is a function of business logic state". It means we define the UI in a declarative way based on the view state.
Yes, the article makes that assertion on behalf of both React and Cocoa and explores the different way they address that problem. It uses "scroll position" as an example: scroll position is typically not tracked in the React state, so how does it work?
In Cocoa, scroll position is part of the view's state, a mere property of the view. This is simple because the UI itself is stateful.
In React, scroll position is typically not part of the state from which we project the view. Instead this state is attached to the projection itself (e.g. a HTML node) and we are dependent on the "Memoization Map" to preserve it. So this memoization is now required for the correct functioning of the app. The "pure function" abstraction is leaking.
Such a dependency isn’t dissimilar from the assertion that some views are stateful, though. I’d much rather have a somewhat pure view (React) than have to deal with syncing or updating state in three different places myself (OOP). I personally treat it as intentional that a view is functional “as much as possible” and embrace the parts where I don’t actually need to track or sync state as something to leave out, intentionally.
I see the “abstraction leaks” as a feature, not a bug. It means React is flexible enough to handle the real world, which is a lot messier when things go async or interactive. Re-rendering due to mouse position without :hover CSS and friends would be incredibly painful.
Also, I have worked on React apps that tracked scroll offsets. This is common (sadly) when managing back and forward state in a single page app as you might want to show a new page on-click and “scroll to the top” but then click the back button “and scroll back to where you left off” and not have the page actually reload for either of those interactions.
Not saying it’s pretty, but it’s certainly possible to need to track scroll offsets. My preference would be to layer stateful scroll-offset views otherwise rendered by pure functions on top of each other and when you go back, just pop a view off the stack, but not all SPA frameworks are designed that way.
> It means we define the UI in a declarative way based on the view state.
If you define your state broad enough everything becomes a declarative/pure functions. That's not the point of interest, it's that React doesn't (and can't thanks partially to the DOM) handle view state super well. You can't make the complexity disappear so without using side-effects like useState/useEffect it lives either the model (current typed text) or the internal state of the DOM node (scroll position).
I spent a while trying to approach this post with an open mind. In the end I was disappointed.
rel/to title: yes. (just say scroll position. fine.) But also I think this is pretty well understood! (No?)
- In declarative UI / FRP / whatever, you model your basic UI state through a pure mapping from your business logic 'model'.
- But (as in procedural programming) the UI has its own state — that's not necessarily part of your initial business/domain/model.
If you want to model it, you'll have to understand it, read it, and add it to you model. That can be annoying—especially if the APIs for getting or setting the state are bad.
This doesn't really sound like a fundamental flaw conceptually to me. It's just an API with defaults you don't have to deal with unless you need to.
- don't care what your color is? fine. system default for you.
- don't care what tab a link opens in? fine. user default.
- don't care how scroll position is managed? fine. system default.
- want to handle any of them? cool. you can.
It sounds like progressive disclosure of API complexity to me. Great.
I'm not computer theorist. But to me React seems like a perfect implementation of MVC. Model is state. View is virtual-dom chunks. And controller is an actual function implementation which maps state to view.
This is typically a good way to organize React projects, in my experience. A few top-level components which hold state with a useReducer and have a few functions to make backend calls, as needed. You could consider the reducer the model and the top-level component the controller. All components that sit below the top-level are pure view components. Props in, view out. They at most hold a minor amount of UI state. They don’t make API calls or update state themselves, that must be done with a callback to the top-level component.
This makes testing simple as you can test the reducer without rendering any UI. The pure view components are also easy to test. Then a few end to end tests to tie everything together.
Interestingly, most of what they claim about MVC is false, and the problems they see with what they call MVC are mostly solved by following actual MVC, others by specific implementations of MVC.
I had only seen these misunderstandings in talks (particularly the "cascading", which true MVC absolutely prevents), which are difficult to refer to, so thanks for the link!
As I wrote elsewhere[1] : "having to say "the problems of MVC are solved by MVC" is less than ideal, because, well, you sound a bit like a lunatic."
This is what bugs me most about these talks. I don’t mind when some guys do something their way, unique or not. But when they spread fucking lies about prior tech to very large auditories, it makes me think about either a conspiracy or their complete lack of competence in this area.
2. Controllers, like views and model(s) are roles, not components. These roles can be filled by the same objects.
3. So people not agreeing where things go is perfectly compatible with MVC, and thinking that's a problem is a symptom of not understanding MVC.
4. I personally try to put as much as possible (and a bit more) into the model. Hexagonal for the win! Naked objects are also pretty nice, though I tend not to be quite that radical.
If the M the V and the C are roles with not fixed functionality or purpose (given that you apparently can put anything you want in them) then what _is_ MVC?
I'm still one of those people. Separation of concerns is a big concern for me.
In my experience with React, it's a lot harder to keep things separated, and developers who I've worked with who have more React experience aren't always up on the concept.
I'm an anti-react by truth, not bias. I'm fine with me-too approach when a company adopts React. But if your company has resources and still pick React, the knowledge and experience of the decision maker is questionable.
This is not coping! It is speaking truth to deniers.
> A core premise of Cocoa, and MVC in general, is that UIs are a projection of data into a different form of data, specifically bits on a screen.
This is a tangent, but the implicit assumption that the UI is visual is just begging for a response from an accessibility perspective, so here goes.
Accessibility is very much an afterthought in native GUIs, not only in Cocoa, but also in Windows with the UI Automation API, and AFAIK with other native accessibility APIs as well. With these APIs, the assistive technology (e.g. screen reader) pulls information from the application (usually via the GUI toolkit), through repeated calls to methods defined by the accessibility API. Often the AT has to do several such calls in a row (and those often translate to multiple IPC round trips, making things slow). And the UI might change between such calls; there's no guaranteed way to get a consistent snapshot of the whole thing, as there is with a visual frame. On the application/toolkit side, these methods may return different responses from one call to the next, and the application or toolkit has to fire the right events when things change.
The web improves on this, in that accessibility information is conveyed through HTML tags and attributes. And yes, this is included in the output of a React component's render function. So while in practice, implementing accessibility may still be an afterthought, it's not an architectural afterthought as it is in native platforms.
One of my goals in AccessKit [1] is to work around this shortcoming of native accessibility APIs, particularly for developers of cross-platform non-web GUI toolkits. In AccessKit, the toolkit pushes a full or incremental accessibility tree update to the AccessKit platform adapter, which maintains the full tree in memory and uses that to implement the platform accessibility API. This even works for immediate-mode GUIs, as one can see in my proof-of-concept integration with the Rust egui toolkit [2].
And no-JS HTML built as div soup is likely worse for accessibility than thoughtfully written React. So React itself has no bearing on it.
My take is that it's the growing complexity of web development (yes, React has some responsibility here), combined with the constant influx of new developers, that's largely to blame for any overall proportional loss in the practice of accessible markup.
I agree. Another thing to consider is that React makes it frictionless to import custom components, instead of attempting to re-write common UI patterns from scratch.
If a React dev leans on an accessible UI framework like Chakra, or Headless UI, or React-Aria, they're probably better off from an accessibility perspective than a dev that tries to implement WAI guidelines from scratch.
And? The point of the parent was that either of those are better than native - doesn't matter how much div soup you have, at least the information exists, unlike the bucket of raw pixels that're all you get from native UIs sometimes.
I think there are a lot of specious comparisons. In Cocoa, all parts of the UI can theoretically participate in the app and be wired in the same way. In the web, you don't get your own text field, etc, so you will have to make do with what's there. Thus there will be parts that have to be handled differently.
I think Marcel brings up a valid point about composition, if I understood it correctly. The composition of interfaces in Dolphin Smalltalk, for example, is super easy with the MVP paradigm. You can build and test a particular feature and immediately drop it into another, larger composite presenter. You just make its model a field on its owner. Other properties can be connected to where they need to be during the initialization of the component.
I'm not sure if wiring up react components is as easy, because you have to think about stuff that is not directly in your data flow. But I honestly don't have enough experience to say. I have more experience with Elm and found it a little less easy.
At that point, the big difference between MVx and reactive is the bidirectional data flow in MVx. Parts need to be bound so that they update the real world, and updates need to be observable so the UI can change. Reactive UI coordinates a filtration of that state through the view painting.
In Elm (old Elm, not sure what it's like now) when you embed a component your top level needs to understand its messages to pass them down/up appropriately. The type system helps you not forget things, but there was a good bit of retyping.
That, and the way that html bits fit into other bits breaks the abstraction. Perhaps using a reactive approach to run Web Components is the best way to get the same sort of composability currently, I haven't tried it.
A UI can be represented as a set of concurrent/asynchronous prompts. UI takes the model and possible next actions, presents this to the user, gets a response, and returns this as an action which is then applied to update the model.
Alerts and login forms are clearly prompts. But so is an complex editor: it constantly prompts the user for the next action, e.g. enter text or change existing text’s formatting. Or a social-media site: possible actions are “login”, “view post”, “create post”, and so on.
Even an FPS can be warped into a prompt-based UI, where the model is the game state (or what the player sees of it), and the prompts are “move, turn, shoot”. But you probably shouldn’t do that. Asp bad examples: a weather or stock viewer where your options are null, and the model is the weather or stock which you can’t change.
Nonetheless prompt-based UI is really useful for many otherwise-ordinary cases. It lets you write a UI like a CLI and automate your UI very easily; it tells you which state is truly part of the “model” and which is just part of the UI; it’s composable spatially (more elements) and temporarily (series of steps). I don’t know if it’s been explored much.
In prompt-based UI, the UI is sort of a pure function of the model. It’s an asynchronous function which takes the model and returns a stream or future of the next action(s).
You’re describing how statecharts model things. I’ve used them to describe and implement user interfaces for at least 15 years. Works great, easy, very few bugs.
Every non-trivial (>10k lines) Cocoa/iOS/Android application I've ever seen or worked on has re-implemented at least some parts of React's data flow model. It's surprising to me that the author glosses over handling lists, which is a huge challenge with this approach, and one that forces you to do some React-like stuff (UITableView cell recycling identifiers!) even if you don't want to.
However, there are definitely some problem domains that don't map very well to the React model. Real time games, for example, have very different requirements.
Even real-time games are often best implemented as a pure function of state. This has a lot of advantages for keeping track of everything, especially in games with a network component. Of course, it doesn't mean the function from state to UI doesn't get to use mutable state - that's often critical for performance. What's important is logically separating the game state from the UI, such that any game state can be isolated, serialized, and de-serialized at will.
I've had a very silly project in progress for a while along those lines to implement a Game Boy emulator entirely in Redux (state) and React (screen output, with pixels mapped to the appropriate bits). If I ever finish I imagine it will be incredibly slow, but it would allow for 'time traveling' the entire emulator state.
Lists are IMO the only area where imperative style has the upper hand - you can build truly endless lists because items are loaded lazily. UITableViewCell identifiers are cell _type_ ids, not cell ids btw, so they pose no overlap with React.
If you need a very long list in React, you need to use a library with quite weird API.
on the other hand, AppKit/UIKit has UITableView/CollectionView view recycling just built into the framework, whereas everyone on web (including React libraries) is trying to reimplement it in userland
>Except for games, UIs are actually very stable, more stable than the model. You have chrome, viewers, tools etc.
I think the author's analogy breaks down here. Almost every point after this is "You don't need this because React started from this faulty assumption that UIs aren't stable".
The hand rails frameworks like React gives you is because this assumption isn't true. If your UI is stable enough where this complexity isn't needed, then don't use React.
> The idea of UI being a pure function of the model seems so obviously incorrect, and leads to such a plethora of problems, that it is a bit puzzling how one could come up with it in the first place, and certainly how one would stick with it in face of the avalanche of problems that just keeps coming.
In my experience, object oriented view systems like Cocoa (UIKit or AppKit) work great for small teams with deep domain knowledge of their code base. However, they constantly require refactoring as teams grow and systems become more complex. This leads to overly complex UI code over time that become brittle.
UI through composition, building UI through assembling components from other components, has done away with those issues. The enforced encapsulation is frustrating that I agree is a trade off, but refactors become simple find and replace to point to new components instead of having to rewrite entire chunks of previously working code.
> UI through composition, building UI through assembling components from other components, has done away with those issues.
it should be noted, you can do this just fine in cocoa/uikit with delegation/message forwarding etc. but most people dont know about that ime
lots of subclassing and overriding is much harder to avoid with view controllers but view-level subclassing is usually an anti-pattern (if i got a dollar for every time i found a "RedButton" class id be a millionaire) but like most things people aren't either experienced or educated enough in these frameworks
> However, they constantly require refactoring as teams grow and systems become more complex. This leads to overly complex UI code over time that become brittle.
i think this becomes true with any system, but subclass-heavy code makes it definitely harder imo
> ...is frustrating that I agree is a trade off, but refactors become simple find and replace to point to new components instead of having to rewrite entire chunks of previously working code.
if done well it is easier in swiftui for sure, but the way i see people write "in the real world" swiftui is used in a much more monolithic fashion and hard to pull-apart... (personally i love swiftui) but i think real-world usage will still he mudballs in a large portion of codebases (there is no silver bullet)
> lots of subclassing and overriding is much harder to avoid with view controllers but view-level subclassing is usually an anti-pattern (if i got a dollar for every time i found a "RedButton" class id be a millionaire) but like most things people aren't either experienced or educated enough in these frameworks
At some point if the system guides everyone into doing it the wrong way, it's a problem with the wrong system. Subclassing is rarely the right solution, one of the things that's really refreshing about React is that most people tend to naturally avoid subclassing when using it.
The thing here is, you can have OOP toolkits that make writing custom components easy. And then you have encapsulation and composition, without needing the React machinery. It is the case though, that the most well known OOP UI toolkits see creating custom components as a somewhat advanced use case and don't optimize for it, whereas in ReactJS/JetPack Compose, it's fundamental and you do it all the time.
The issue I have with react isn't the declarative views.
It's the automagical rendering.
I am unable to reason about how even a fairly small react code base renders and updates itself.
In my own projects, I've had a lot more success with lit-html - which is basically the declarative view layer of react - and controlling the rendering myself, usually with simple observables.
Also helps to avoid the react anti-pattern of having your views full of state wrangling.
This is a big reason why I've switched to SolidJS. It's extremely similar to React from the developer's perspective, just architected with cleaner primitives so you understand what's happening in precise detail, and can also use the primitives to do other types of reactive state if you want.
I can sympathize with this, and I personally love React. It's very easy for components to start diverging from "the" state (some global state) by holding their own internal state (useState).
I encounter this in situations like: I want to open a drawer when an input is focused; the global state doesn't need to know about every input with a drawer, so useState on the component is useful. Already I've diverged from a global state. And I want something else to happen when the drawer opens (maybe an async network fetch, loading indicator, error messaging if the network fetch fails...), so I add useEffect, and now I'm adding side effects into the mix, and it becomes very difficult to reason about how the component got into its current configuration at any given time.
At work I have these components that I imagine will start small, that tie into some global state, but end up needing to keep their own internal state that grows and grows, and I have useEffects all over the place watching for other results of side effects... it's a gigantic mess. I don't know what the solution is. UIs are hard
> the global state doesn't need to know about every input with a drawer
I like this example because it is exactly how it happens in real life every single time in my experience.
It does not need it, purely an aesthetic reason. Maybe it will perform worse if it does (maybe). If we commit to the model that we represent everything in global state (singleton) this problem disappears.
Let's say we keep some state for that drawer and pass it to the global state. Then we would keep a global state that is always current and an ELM-like model that is easy to understand (https://guide.elm-lang.org/architecture/)
For some reason that commitment is the first thing to drop, and usually not even for practical reasons, but for aesthetic ones or hypotheticals.
It seems to me it is this permanent fight between flexibility and correctness. If we want correctness we should stick IMO to a simple model (let's say ELM architecture, for example) and ruthlessly apply it, no exceptions.
If we want to use context, redux, local state, mobx, some direct DOM manipulation (as I see sometimes) then it is no wonder we cannot prove its correctness.
Most people find it difficult to accept constraints, even for their own good (type annotations, even using git). I still remember a project where I introduced git and after a few months of leaving an ex-colleague (still in touch) proudly announced that they got rid of it because it was slowing the team down.
"All of humanity's problems stem from man's inability to sit quietly in a room alone" — Pascal
Exactly! I don't know the right solution, either. All the tutorials I've read don't seem to know, either. They're either too simple to where that doesn't become an issue, or they do what you do, and it looks like a mess to me.
In Vue, I'll usually create a service to handle local-yet-shared state (which is just Vanilla JS, no Vue constructs), or create a VueX module (which you could use in React, as well). It's much easier to follow than React's wrapper methods.
In my experience, in both React and Angular, a lot of the things I expect a framework to handle need to be managed by the developer (namely, keeping shared values in sync). Over time, patterns can deviate to where different components/modules use different patterns, and devs spend extra time reading through component files to figure out why their feature isn't updating as expected.
I would strongly suggest reading through my extensive post "A (Mostly) Complete Guide to React Rendering Behavior" [0], which walks through what "rendering" actually is, what triggers it, React's default behavior, and ways to optimize the behavior by skipping renders for certain components. The post "When Does React Render Your Component?" [1] is also excellent.
The short summary is that React queues a render when you call some form of `setState()`, renders this component, and by default recursively renders all children inside this component too. Everything else is variations on that idea.
There's also the old-school FRP approach of "behaviours", which are pure functions of time (where "time" is an abstract datatype with few operations). Combining and composing these functions gives new behaviours, until we have a single top-level "main function"; which the runtime system samples as needed (e.g. at a steady framerate, or varying to save power, etc.)
Conal Elliot has been working on this since the 90s (e.g. http://conal.net/fran ), but has since rebranded it as "denotational design", since the FRP label now refers to more discrete, event-based systems like Elm and React.
Unfortunately, the API of early implementations required old states to be kept for unbounded amounts of time, which made them impractical; although newer implementations like FRPNow don't have that problem.
I have been thinking about this a bunch lately and I think that the main problem with all these approaches is that it's all built on top of a DOM and as a result it gets us to a local optimum and prevent us from reaching a global optimum.
The thing that these approaches don't account for is the rendering aspect which should ideally be done on the GPU.
There's this duality between Array of Structs and Struct of Arrays [0]. People find programming with Array of Structs easier but GPUs work with Struct of Arrays. I think that a language is needed that fundamentally compiles AoS to SoA.
Another advantage is that repainting the whole scene is much cheaper do you can be a lot more aggressive with repainting everything.
>The application event loop coalesces these damage rectangles and initiates an optimized operation: it only redraws views whose bounds intersect the damaged region, and also passes the rectangle(s) to the drawRect:: method. (That's why it's called drawRect::).
So, according to this, you need an explicit run-time mechanism to optimize the interaction of parents with children, with some form of coalescing.
Antithesis:
>This is another reason why it's advantageous to have a stable hierarchy of stateful objects representing your UI. If you need more context, you just ask around. Ask your parent, ask your siblings, ask your children, they are all present. Again, no special magic needed.
...except here, where OP says you don't need any special magic to deal with parent/child relationships: just reach up/down/left/right and do your thing!
Synthesis:
The reason one-way data flow became so popular is exactly because the second view is a trap, or at least, a local maximum. If you are really doing complicated orchestration in your hierarchy, then you will end up reinventing the first wheel, to avoid expensive/inefficient parent/child/parent thrashing cascades.
React does in fact have a good solution for this, in the form of contexts. These also allow you to reach up and go talk to parents, in a controlled yet composable way. The way I describe them is "singletons but with scope". An underappreciated benefit is that you can extend a parent context and pass it down, replicating some of the benefits of inheritance, without the messyness of subclasses.
Where React falls short is that parents can't respond to children without re-rendering themselves, which ironically makes implementing e.g. nested tree widgets hard, even though the result is all tree-shaped.
Functional effect systems which can re-join after forking, and gather yielded values, do not have this issue, and I wish React would explore this avenue instead of the all the React 18 stuff they've been doing lately... it feels too much like they are leaning into the same Facebook "dumb read-only UI" style that the article correctly laments. They are straying away from React's original appeal, which was that it was "just the V in MVC", and instead becoming very opinionated on how server side and client side should be combined. Meanwhile the challenges of how to replicate complex legacy desktop UIs remain mostly unaddressed, with the official advice about event handlers, state and effects falling short by a mile.
I tried to read this article, but the authors bias was apparent right away, so they lost me. The points are too subjective. I didn't read any objective different between the two paradigms.
This is a good counter-argument to the React proposition. It's 5 years on, and both Apple and Google are now adopting the model for themselves via SwiftUI and JetPack Compose, but, is it actually the best way to do things?
If you're used to toolkits that give you a basically fixed set of components, in which creating new components is considered an advanced operation and there's no assistance with data binding, then something like React seems like a breath of fresh air. I've done a bit of JetPack Compose, and also some JavaFX and of course HTML5 stuff. On the web React is an upgrade simply because the DOM isn't meant for UI and HTML has no notion of components at all. On Android, well, the classical Android UI toolkit was never that great. It's a Java Swing era API with three different clock classes in it! I can't comment on UIKit, never used it.
Maybe a better comparison would be to something like JavaFX, which pre-dates the crazy for functional programming but benefited from the years of experience with classical toolkits like Swing/GTK etc. And here, it gets hard to really get excited by React. The blog post isn't wrong - the React model requires a ton of concepts, abstractions, and complicated infrastructures to try and recreate things that come for free in OOP toolkits. JavaFX supports data flow and binding because every property is both observable and bindable via lazy dataflow graphs. So your UI can indeed be a pure function of the model, but instead of re-execute/cache/memoize/diff, you assemble the computation graph for whatever needs to be reactive. Most of the time, you don't actually need to do anything complicated here and can write code as a straightforward imperative calculation.
Creating components is likewise pretty easy, albeit JavaFX is currently in a sorry state where the open source community around it doesn't maintain the documentation properly, so it's hard to link to a tutorial that demonstrates this. But basically you get a pay-as-you-go approach in which you can just group some widgets in a class, or you can be a bit more ambitious and inherit from Control which gives you things like proper focus/tab order management, the ability to control properties using CSS and so on.
When I read code written with JetPack Compose, the feeling it gives isn't really that great compared to the OOP style. The way it totally changes the core programming model of the language looks like an absolute nightmare to teach to new programmers. The most fundamental rules of the programming language are suddenly up for grabs - what looks like straight line imperative code actually isn't, variables might retain their state beyond the scope of a function call ... or might not ... and it seems like React/Compose codebases invariably degrade into tons of tiny functions passing giant amounts of stuff around on the stack, with lots of obscure performance problems that come from the game playing with the core execution model and all the diffing/patching going on behind the scenes.
In the end I find I agree with the author. Good UI is not in fact a pure function and the FP model is a poor fit. There is too much implicit state that is best encapsulated in the UI framework, and with an actually well designed OOP framework data binding just isn't painful enough to justify the costs.
Note that this is about pre-hooks react, see also the response from Dan Abramov at the bottom of the article. A lot of criticism isn't valid anymore in the hooks-style react (although it comes with is own problems).
Of course, we can put a launchMissiles inside useEffect, so of course UI is not pure function of model in React. That's why there's PureComponent in React which disallowed state or effects.
It's not about useEffect. If a Component renders a text field, that text field has additional state (e.g. cursor position) which is not part of the model. That's one reason why the UI is not a pure function.
Couldn't this be solved by acknowledging that there is application-domain state (such as the value of the text field) and UI state (such as the cursor position and selection), which can be represented by an application-domain model and view model respectively, rather than trying to pretend that the latter doesn't exist?
Although if you model all the UI state, maybe you just end up with the DOM?
No, this implicit state is a very practical concern in React. For example see this highly upvoted SO post. Text fields lose focus on re-render; the solution is to use a unique key so that the view gets re-used and the implicit state is preserved.
> 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.
So smug that it's hard to take seriously.
Especially when even Apple is switching to SwiftUI, something that is the opposite of Cocoa and more similar to React.
Though I wonder how someone with both Cocoa and React experience looks at React and goes "Oh, it's the old-school Cocoa way of doing things!"
This blog post reminds me of HN four years ago when everyone had their little smug epithet about Javascript and web developers, those idiots who are too amateur to see the beauty of Cocoa and winforms or whatever most of us are happy to swap with something better.
Agree that it's phrased really smugly and that detracts from the message, but important to note it's dated 2018, at which time React was very different than it is today and SwiftUI didn't exist yet
I'd argue that React is pretty much the same now as it always has been. The ecosystem has matured and grown for sure but even the basic API has remained pretty much stable.
Hooks didn't exist yet, for one, as mentioned in the Dan Abramov quote at the bottom:
> we’re adding a stateful function API as a preferred alternative to classes soon
Class components were the focus at the time, and are now all-but-deprecated. Also, Redux was still very popular in practice, which it isn't so much now (partly due to hooks)
That's a pretty big change in the way people use React, which certainly informs a discussion like this
I'd have to disagree with this on a couple levels.
Yes, hooks change the way we write React code in some ways, but in a lot of other ways nothing has changed. We still write components that accept props, have state, and return UI descriptions as JSX elements, and those components may cause side effects after a render is completed. Conceptually, the core principles of React are still exactly the same, and hooks didn't change that.
Additionally, Redux is still by far the most widely used state management tool for React apps. My rough estimates are that 45-50% of React apps use Redux, whereas Mobx and XState are around 10-15%.
I'll agree that Redux is not as "popular" as it once was, which is due to a number of factors. It was heavily overused early on, and the ecosystem has expanded to include a lot of other great tools that overlap with some of the use cases for Redux. But, "modern Redux" with Redux Toolkit is much easier to learn and use than the legacy hand-written patterns, and we get highly positive feedback on a daily basis from folks who tell us they enjoy using RTK. (In fact, RTK by itself has more downloads than Mobx, XState, or React Query.) So, that tells me Redux will continue to be widely used for a long time.
> My rough estimates are that 45-50% of React apps use Redux, whereas Mobx and XState are around 10-15%
Curious how you arrive at these estimates? I would have thought fewer than 30% of React apps started in the last year or so use Redux, but of course, many still do, perhaps a higher percentage of apps that were built >2 years ago do (you could argue these are more mature, so they've "grown into" redux, though you could also say they're legacy and preceded newer React capabilities and tooling)
I don't have a strong inclination one way or the other, and have been toying with introducing Redux (or some other kind of state management) for the app I work with, but my understanding from the general sentiment of the threads/projects I follow is that market share of Redux has fallen in the last year or 2.
Just looking at download trends redux is literally in a class of its own. What I do wonder is how often people are abusing context, state, and reducers to make their own terrible state managers rather than using redux? I bet that number is higher haha.
Hiya. As mentioned in my other comment, I decided to write up a blog post about all the different potential metrics you can use to estimate package market share, flaws with those metrics, and how I use them to guesstimate React state management lib market share (Redux et al):
The biggest takeaway here is that based on these numbers, I'm actually going to have to revise my "45-50%" estimate that I've been throwing around for the last couple years down to about "33%". React downloads have continued to go through the roof, and they've finally separated more of a gap from React-Redux downloads. There's also a surprisingly large differential in terms of Github "dependent repo" numbers.
That said, Redux is most definitely still the most widely used state management lib by a mile.
My main guesses for the changes are that more folks _are_ just using React state and no separate state library, and possibly some influence from number of learner repos.
and unfortunately you asking me about this is tempting me to turn those comments into a blog post with some additional thoughts :) (I.... may actually try to do that tonight or tomorrow. If you're interested, keep an eye on my blog at https://blog.isquaredsoftware.com .)
I'll definitely agree that Redux usage has peaked in _relative_ terms, although as you can see from the download numbers it seems to still be growing in _absolute_ terms. Also it's entirely possible that fewer new projects are choosing Redux.
Then again, how do we even count "usage" in the first place? I've seen Web3 app boilerplate repos that include Redux Toolkit. If 1000 people clone that repo and play with it, how do we compare that usage conceptually vs one app using Mobx that's been around for years and has a bunch of developers working on it daily?
As I've pointed out in a number of podcasts and articles: I'm not trying to convince people they _must_ use Redux, or even that they _should_ use Redux. I just want people to be aware that modern Redux is way easier than legacy Redux, that Redux _is_ still widely used and is a viable choice, and what some of the tradeoffs are when using Redux or any other state management library.
I've actually been trying to get the community to come together and work on a centralized site that would list tools in different use cases and categories such as state management, styling, data fetching, and build tooling, describe purpose / use cases / tradeoffs for each tool, and have that as a recognized resource for people to use when researching what to use for a project. You can see the original RFC discussion and prototype site here:
Sadly I haven't had time to push this forward, and it needs to have more people involved and helping fill out content on the various topics (not just me).
If your goal is to increase RTK adoption, rather than being reactively defensive whenever someone writes something remotely negative about Redux, may I suggest focusing on the highly positive feedback you mention? Writing up case studies on how RTK helped solve concrete problems, instead of being summoned anytime someone is wrong about Redux on the internet…
Anyone judging Redux differently if it has a 15% or 45% market share is making decisions based on the wrong parameters anyway.
I've debated throwing together a docs page with a bunch of quotes and links to people saying how much they enjoy using RTK, but I've got enough other stuff on my plate that it hasn't been a priority.
It takes quite a bit to wrap your head around the SwiftUI way of doing things. I think a lot of people get frustrated and throw in the towel. I'm sure React is the same story.
And while you can't do everything in SwiftUI, it's pretty solid and capable now. I only have a couple things using AppKit in a large macOS project.
It is fine for small toy apps (think the calculator), but it is missing a lot of functionality to build a full fledged app. Think collections, which is the backbone of most apps.
UIKit is very mature and has evolved to a really good UI platform. SwiftUI is not even close to unseat it. Maybe in few years it will mature as well, but for now UIKit is the way to go.
I only ever played around with making a toy app while following along with Stanford's Swift UI course[1], and even just the basic things I was doing there, I ran into multiple frustrating/confusing bugs.
As if it wasn't difficult enough to wrap your head around learning it in the first place, running into bugs makes it even harder because you think you're doing something wrong until you finally ask online and get told by people more knowledgeable than you, "I don't know why that's happening, wrap your view in a container and it'll work"
At Beam[0] we are building a web browser that uses SwiftUI almost everywhere except for the text editor.
So far its been great to build UI and animations with. Each release gives SwiftUI more apis and features, so there are less and less older style apis to wrap in SwiftUI ourselves.
"The core idea with react, and this is the only thing that matters, so don't view components as a react innovation. Components have been around forever. You build any sort of native application on Windows or macOS or iOS and there's a notion of component, some call it a view or whatever, but the idea of composing components out of other components has been around since the dawn of time.
I don't know why we weren't doing it on the web. I think we were just, I don't know, being stubborn or something. But that's not a new innovation."
Hmm...sounds just a bit like what they were trying to achieve was something along the lines of "Cocoa for the Web".
But what does he know?
¯\_(ツ)_/¯
Anyway, he does go on to say that the core innovation is having the UI be a pure function of some state. But that's the part that isn't actually true. It ain't a pure function. See Dan Abramov's comment:
Support for local state and side effects is absolutely a core feature of React components and not something we avoid for “purity”.
So if it's not a "pure function" of the state, then it's some mapping of the state. All UI is some sort of mapping of the state, otherwise it's not really a UI.
Hmm...
So being a "pure function" of the state is not the innovation, because it isn't true, and being some mapping of the state is also not the innovation, because all UI is a mapping of the state.
Hmm...
So what is the actual innovation? It is more or less directly and visibly expressing the UI in code. And since all our languages are essentially procedural (functional, OO) that means expressing the UI as a procedure (function/method).
And so the "pure function of the state" turns out not to be a feature of this model, but a (squishy) requirement. Because in order to make the UI definition a procedure, you have to re-run that procedure at arbitrary times (and ideally also run it partially). And so the requirement is "sufficiently pure that we can re-run it".
And that is an innovation, and it's kinda cool. But it does break down rather quickly, which is why all these frameworks keep iterating, and iterating, and iterating.
The best definition i can think of for declarative UI frameworks is probably something closer to 'frameworks that optimize for your ability to make a pure function representing state'.
(After all `[[UIViewController alloc] init]` does indeed purely say 'you have view controller' — that's just not super granular!)
In this light, isn't SwiftUI just a setup more optimized in this direction
That isn’t innovation either, it’s just game developers who are too busy to tell us immediate mode gui stories all day. The only innovation it has is solving the problem natural to the web only – dom changes suck, amplified by a historical lack of a cell concept (which is a core disaster). They partially “fixed” that by diffing vdoms. Everything else is just opinionated bargain on top of that.
Basically an element without its own geometry that renders in multiple locations on the screen. E.g. if a dom table (or a grid) has 10 columns x 1000 rows, it must have 10k real td (div) elements. A table in most desktop frameworks will only have 10 cells which will be rendered at multiple (visible only) y-positions. When you scroll, they render a few in advance on a scroll-cache surface. This saves memory and reduces reflow pressure because a framework knows the geometry of a cell.
Of course if a table (any collection really) contains differently looking items, it may have more than 10 cells, and rendering controller may choose between them based on which row/column is rendered.
React mentions in their docs a plugin which simulates exactly that (forgot the name, something like react-dynamic-scroll).
Also, some frameworks use cells to render active elements basics, e.g. an input without a frame, or a check mark cell without a background. This helps to make controls editable without instatiating a real input/check/button element at the coordinates of a cell. This makes every button in a container a single button which signals clicks to its container, which based on x,y knows which item that is and signals rendering controller that item X was “clicked on the button B”.
Wow, I've been trying to come up with a way to do this on the web for a while now, but haven't cracked it yet. Had no idea it was a common thing in other UI technologies.
> Especially when even Apple is switching to SwiftUI, something that is the opposite of Cocoa and more similar to React.
Well, that's hardly a counter argument. The core argument should be whether "SwiftUI is better than Cocoa", and not whether a Big Tech company is switching to it unless we are in a popularity contest.
I’ve heard that the original React was built in SML, rewritten in OCaml, and later transcribed to JS (and now is developed in ReasonML)[1].
We shouldn’t downplay the unmitigated[2] disaster of language design that JavaScript (and more broadly, the engineering disaster that dynamic typing) is.
Just the fact that React was/is created by a person who is functional, static, strongly typed language aficionado is a testament that good things in the JS world comes simply because of its entrenched status due to being the only directly supported language in browsers — not because of anything inherently good about JS.
There were a lot of “MVC in JavaScript” frameworks before react. I don’t think that’s really a useful description of react or of the work or idea involved.
Backbone and Marionette were MVC for the web, and they have basically died out. React is a child of the Flux architecture philosophy, also developed by Facebook. [1]
MVC examples in this article do not represent MVC in Cocoa. E.g. last time I checked cocoa tables were bound to array controllers, which controlled filtering, available actions etc. Not to bare models. Older-style cocoa mvc rendering was also based on the view<->controller connection, which may act as a “store” or do something more sophisticated, depending on what you write in delegate methods. These comparison graphs always involve some “head & shoulders” vs “grey regular shampoo” style narrative.
And now four years later they accepted the web developers but now are having smug epithets about Web3 developers, those idiots who are too idealistic to accept the beauty of Web2’s centralized model
No indeed. However the UI does emit changes to the model as the user interacts with it. These can be modeled as a series of values over time, aka a stream.
So obvious they are left as an exercise for the reader?
This whole article seems to be based on the idea that pure functional programming is absurd and impossible, and this is just obvious and doesn't need to be shown. For someone who has written a decent amount of Elm, it certainly isn't obvious to me.