React has hooks because React's "classes" were pretty crappy…partially due to crappy OOP in general in previous eras of ECMAScript. But instead of actually making their syntactical sugar better as JS' native OOP paradigm improved, they went the <i>opposite</i> direction. After having to use them in a large React codebase for years now, I still can't stand them.<p>For a look at a UI library that actually rocks <i>precisely because it embraces</i> 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 <i>any day</i> over React.
My first boss had an often repeated mantra in code reviews: "akdor, don't fight the damn language!"<p>If you're using Python, write with Python concepts. In C#, write C#. In F#, write F#. Etc etc.<p>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.<p>Don't fight the damn language.
React breaks down because state comes in from multiple different directions and at different times - the history API has its own state, the query to the server has its own state, and the view has its own state, and they all happen at different times.<p>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.<p>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).<p>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.
I really don't like hooks, because they are so magic. The old class based way of doing things was much more explicit. 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.<p>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.<p>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?
The functional programmers always sound like wannabe mathematicians to me, with their fancy category theory words. And the OOP guys always sound like wannabe engineers, with their fancy AbstractFactoryFactory.<p>Nice article though.
Interesting take, but I find it much more insightful to think about how you’d simply implement a hook (not any of the React specific ones). MobX uses similar mechanism.<p>user declares foo with a call to “useState”<p><pre><code> function useState(…) {
useStateCalls.push(…)
}
useStateCalls = []
foo()
// do something with useStateCalls
</code></pre>
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:<p><pre><code> function BazComponent(props, hooks) {
hooks.useState(…)
</code></pre>
It would be less magic, more boilerplate (especially with custom hooks). That’s it really.
Really interesting to see the various examples/incarnations the author went through to illustrate his point. Very well done.<p>I must admit I like his aphorism: "If you're honest while making your code better, you will inevitably end up making it functional".<p>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<p>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?...
The author discusses the sequential leaps towards functional programming: from mixins, to HOCs, to render props, and finally to hooks.<p>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?
I always considered hooks to be a poor-man's immediate-mode GUI, rather than some kind of pure FP thing. It's not immediate-mode at all, but there are similar benefits to just being able to produce UI in the middle of the interactive loop.
> Well actually this is not an aphormism, it's not even a propositional statement, because “X is a monad” is just math-speak for “X is composable”, just like “Y is a functor” is math-speak for “Y is mappable”.<p>Nah. Monads are one relatively special way to compose stuff. And monads don't even compose very well.<p>To give example: the weaker applicative functors (to use Haskell's terminology) compose much better.<p><a href="https://en.wikipedia.org/wiki/Applicative_functor" rel="nofollow">https://en.wikipedia.org/wiki/Applicative_functor</a>
I'm not well versed in React so only clicked into this article out of mild curiosity. If anyone is interested in discussing the non-technical aspects of this submission, please continue reading this comment :)<p>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?
Sometimes I look at what's going on in React land and just want to ask these developers, do you really need all this to make a UI? Really? Are you sure? Are you making things better?