For a look at a UI library that actually rocks precisely because it embraces OOP and the native object graph of the DOM, check out Lit. It takes everything that's awesome about browser-native web components, then simply adds some fabulous DX on top for reactive re-rendering upon prop updates. I've never used a UI library in my life that's meshed with my brain better than Lit. I'll take a good Lit-based web component class any day over React.
useEffect forcing you to clean up after yourself reminds me of RAII from C++. IMO, any "ugly" functional component with hooks is even worse when written as a class component.
I actually feel much the same way and have vented about some of my frustrations before: https://blog.kronis.dev/everything%20is%20broken/modern-reac...
However, that amounts to precisely nothing, because the industry has decided to use hooks moving forwards and they're basically inevitable, because even if you'd ever want to use something like class based components, the libraries that you'll try to use and integrate with will still make you use them sooner or later, because it's actually not easy to maintain support for a wide variety of ways to use your library.
In the past few years, i've increasingly felt like i should look at either Vue (which i already use at my day job) or Svelte, or even Angular for my personal projects.
Curious, what do you use for state management across WC’s?
If you're using Python, write with Python concepts. In C#, write C#. In F#, write F#. Etc etc.
React has many many great ideas. But they are perpetually fighting the language. Here we have a great demonstration of stuff that is built in to any ml-legacy language being hacked into js in a way that the transformation happens in the user's browser in a scripting language at runtime! It's both genius and ridiculously inefficient and alien.
Don't fight the damn language.
JavaScript is the browser's machine code; having it as a compilation target makes at least as much sense as having an Intel chip as a compilation target.
ok, but it will just get compiled down to machine code?
When a page changes in the history, the state of the view must change to reflect the history state, but when the user changes the state like going forward in pagination, the state in JavaScript is changed first, then the history state must be changed, then you have to figure out which state caused the change so not to get stuck in an infinite loop.
When a user initiates a new query, the query state changes, which triggers the request to the server, and then the results come back, but the query state may change when the results come back, which would trigger another infinite loop. Eg, when total_count is unknown, and then it becomes known with a query and must be included in the query state for optimisation, etc (don't try to get total_count second time, etc).
That is the problem with React - state has timing issues. I hate it and don't use it anymore. I just use pure JavaScript github.com/thebinarysearchtree/artwork. Half the code of react with orders of magnitude more performance just by using standard JavaScript stuff. It is hard to argue with that, but people will try until it can't be denied.
Actually, class-based components have some annoying magic, too. The type of the object you create when you write `<MyComponent>` in JSX is different from the component class you write. There is some wrapper around it IIRC.
I wonder what React would look like if you get rid of all the cleverness and hidden state, and keep JSX and the reconciler as only magic?
They weren't though. They looked explicit, but what was actually happening wasn't what appeared to be happening. Dn Abramov wrote a good blog post about the subtle bugs that can sneak into class components - https://overreacted.io/how-are-function-components-different...
At least, hooks should take some kind of "context" and "name" parameters so that they are in spirit a pure function. Then you could also call them in loops and if blocks without problems.
Dan also addressed a lot of the 'why are hooks like that?' questions in another post that goes into some of the designs the React team rejected. https://overreacted.io/why-do-hooks-rely-on-call-order/
Hooks can be hard to reason about but they make code a bit less error prone because when they don't work they fail completely. Classes don't. Classes work most of the time. That is so much worse.
Nice article though.
user declares foo with a call to “useState”
function useState(…) {
useStateCalls.push(…)
}
useStateCalls = []
foo()
// do something with useStateCalls
It’s a neat syntax sugar, it explains why they can’t be called conditionally, it allows for a clean api. If every component received a “hooks” argument, like this: function BazComponent(props, hooks) {
hooks.useState(…)
It would be less magic, more boilerplate (especially with custom hooks). That’s it really.That said, `hooks` as an argument wasn't chosen for a couple reasons:
- It doesn't work for custom hooks, which need to be written as separate functions
- Function components still receive the legacy `context` object as their second parameter. (The long-term migration plan is that someday they will instead receive a potential `ref` object instead, and that will allow removing the `forwardRef` API.)
Dan Abramov wrote an extensive post on why various alternative hook API designs were rejected [1], including the design criteria and constraints that they were looking for.
I tried to get into MobX years ago but found it impossible to get along with. I don’t know what it’s like now, but the documentation back then failed to explain the magic. It was just like “Hey, look how convenient this is, just follow our instructions and you’ll be fine (and don’t think about how it works)”.
Then some time later React came out with hooks, and they took the time to introduce the concept carefully, to explain how it’s actually really weird and non-idiomatic and principle-breaking but leads to all these ergonomic benefits, just make sure you understand the weirdness and keep it contained. They give you the information you need to change it in your head from ‘magic’ to ‘smart compromise’.
I must admit I like his aphorism: "If you're honest while making your code better, you will inevitably end up making it functional".
Although I would tongue-in-cheek add a suffix of "...you will inevitably end up making it functional, but stop once you start making everything a monad". :-D
I feel like FP purists hold "...and now it's a monad!" as an forgone conclusion / must be achieved end state. ...which, I did enough Scala for comprehensions (and now JS promises) that I believe I basically "get" monads, but so far I've not seen the light about the differences between "good enough" lazy abstractions (JS promises) and "Shalt Follow All Of the Rules" real monad abstractions, other than the latter end up letting you (sorry, tongue-in-cheek) write even more obtuse Haskell?...
I’m very comfortable with hooks, but I’m not much of a functional programmer beyond that. So my question is: what is the next step in this progression? What is after hooks?
There are already pretty good ways to manage state to choose from, from mobx/reagent-style of mutating a store and things just re-render automatically, to observables/swiftui's compose reactivity, to other flavors of reactivity like Elm.
They all are better to work with than what I was doing not long ago with uikit/backbonejs/whatever. It's never obvious when you're taking on the right trade-off between boilerplate and reactive magic, though. But times are pretty good these days for building a rich UI.
While each item in the series is "more functional" in some sense, the progression itself is not something reflective of some kind of standard progression of concepts or techniques within functional programming.
I'd say mixins have nothing to do with it; HOCs do in the sense that they're similar to higher-order functions (I suppose they literally are if the component is a function component); render props is pretty much purely a React concept to my knowledge, hooks I've heard described as "similar to" algebraic effects (this seems like an accessible article for more info: https://overreacted.io/algebraic-effects-for-the-rest-of-us/).
So I don't think there is any logical next step. To me it just feels like a series of individually thought out refactors, each leaving us with a system that's a bit more clean and compact than the last.
More broadly speaking though, I think any significant such changes will have the property of reducing impedance mismatch between React and some Platonic ideal of functional reactive programming (applied to html/css generation).
I wonder if there is an equivalent of the Turing tarpit, but for languages that aspire to confuse with ever-increasing relationships to tangentially-related mathematical concepts. The Church tarpit? Or would that be the Lambda tarpit...
It sounds like continuations to me. Or Common Lisp conditions/restarts. But that would be an implementation detail when talking about React hooks. And if I'm brutally honest, that all sounds like a retroactive analogy to move hooks into the territory of functional purity or relevance to FP. Better than having people realize it's all a pile of ad hoc design choices on top of JavaScript.
> So I don't think there is any logical next step.
We're already transpiling everywhere. Just get rid of JavaScript! Svelte did it. TypeScript did it. Instead of brutalizing functions, React could instead be forging a new path in language design.
The article even links to more material about FRP.
Nah. Monads are one relatively special way to compose stuff. And monads don't even compose very well.
To give example: the weaker applicative functors (to use Haskell's terminology) compose much better.
I'd say there are many different notions of composability. It's partially a matter of taste to elevate one of them to be The One and Only Composability.
But only partially. Partially there are also somewhat objective notions, or at least notions shared between different subjects, of why one thing is more properly composable than another.
Eg it's pretty uncontroversial to say that a program build out of functions composes 'better' than one build from loops and mutable variables.
From a wider point of view the problem with monad composability is exactly that they require a special kind of composability on their elements (join or bind), which makes them less composable with each other.
What do people think of the foray of these blog posts with embedded javascript components? It appears that this a nice way to provide interactive explanation on subjects. I think this post does a nice job. That said, the website experience is always a little off when someone uses components like this. For example, if you open the page and quickly scroll down, you get lots of flashes of the page layout changing. Also, the back history button seems to be broken after visiting this page. I think the website pushes itself to the browser history or something?
But I barely notice the flashing / layout changes on this 2009 iMac I'm using at the moment. It's surely worth the ability to show live code that I can see and play with. It's a lot less disruptive than having to open up a React-ready playground myself and copying in the code, that's for sure.
I'll take this over static code any day when the point is to show the implication of live code as it's done in this sort of blog post.
This only makes sense for a very narrow conception of the range of UI problems. If your UI is just a basic form or trivially reflecting database structure in a CRUD app, then you would have little to no need for React.
If you were to try building e.g. the full Gmail UI and make the attempt with e.g. document.querySelector (or jquery) and again with React you would have a painfully clear understand of the necessity. (And I've selected Gmail here not as an extreme example, but rather as typical of the kind of thing I've seen React getting used to build.)
As far as needing "all this," there seems to be a misconception: React is actually quite compact. If you take into account its entire history (as you might have to if maintaining a legacy codebase), you'd find some inelegant redundancy (mostly just supporting both class and function components)—but if you're just grabbing the latest version and using it for an appropriate problem, I think someone with a sense for good system design would have few complaints.
It seemed to take a lot longer than normal to get comfortable with it as a framework. React still feels like a clever hack sometimes.
That said, I think React is fatally flawed in its necessity to treat state in immutable structures. I wish React could be more like an immediate mode UI library, or a “thin” rendering layer as it was originally advertised. I want to be able to structure and handle my state however I please. I think such a library is bound to come along and topple React.
I'm dealing with thousands of fields with hundreds of different validations (usually min/max values or regular expressions), React is not the only one that can solve this, but at the moment I don't regret my decision. And with hooks and functional components, they're really compact - compared to e.g. Angular, which is more aimed at Java/C# developers I believe in terms of formality and verbosity.
The fatal flaw of React is that there are two states - client and server. Very often two different languages. Two different teams. Long and painful build. And in any case, you cannot trust the client. In 99.99% of the use cases - apart from extreme scale and full page renders - a LiveWiev will be more than enough, at 1/20 of the complexity and development time.
And yes, we use React.