15 ms·
Hooks: React’s Do-Notation
- bigyikes 5y agoThe author discusses the sequential leaps towards functional programming: from mixins, to HOCs, to render props, and finally to hooks. 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?
- hombre_fatal 5y agoI don't think anyone knows, hence all the experimentation going on in the space. 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.
- eru 5y agoYou can have a look at functional reactive programming for ideas. The article even links to more material about FRP.
- jitl 5y agoFRP systems like RXJava, Combine, etc to me seems like an enormous and extremely convoluted kitchen sink of abstractions that I’d much prefer to avoid dealing with. From the outside it looks like a Turing tar-pit dressed up with fancy suspenders and a top-hat. It does not feel declarative to me; instead of declaring that function X needs data Y (and should recompute whoever Y changes), I instead need to set up a pipeline of steps as crazy as a make file that hopefully output Y at the right time. ??? Maybe I’m just looking at bad examples, but it really seems like plumbing a Good Compiler should do for me. At least Jetpack Compose is throwing the Android developers a rope out.
- christophilus 5y agoSame. Every Rx codebase I’ve seen has been nightmarish to contemplate. I’d rather program in brainfuck.
- eru 5y agoI don't think RXJava has the 'F' component of Functional Reactive Programming? I've worked with some rather nice FRP-like systems in Haskell. I doubt you could make this work in Java without losing your mind?
- westoncb 5y ago> from mixins, to HOCs, to render props, and finally to hooks. 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/ 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).
- deckard1 5y ago> I've heard described as "similar to" algebraic effects 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.
- eyelidlessness 5y agoI would highly recommend learning Clojure. Hooks will feel familiar to reference types and the rest is a really good introduction to FP without being absolutist about it.
- blindseer 5y agoI'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 :) 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?
- hombre_fatal 5y agoI couldn't reproduce issues with the back button. 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.
- ovao 5y agoI had some issues with re-scrolling here on Safari, but not too bad. Probably a minor fix.
- dwaltrip 5y agoThe latter half of the article had some quite bad scrolling problems for me on mobile safari.
- aaronbrethorst 5y agoI’d be much happier if that blog post’s text wasn’t a low contrast yellow on a black background. I switched over to reader view and didn’t see any JS components.
- xixixao 5y agoInteresting 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. 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.
- acemarke 5y agoAgreed that the actual implementation of hooks is _relatively_ "simple" internally. I strongly recommend Shawn Wang's talk/post "Getting Closure on Hooks" [0] as a good dive into the implementation approach. 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. [0] https://www.swyx.io/hooks/ https://www.swyx.io/hooks/ [1] https://overreacted.io/why-do-hooks-rely-on-call-order/ https://overreacted.io/why-do-hooks-rely-on-call-order/
- playpause 5y agoFor me the issue is whether the magic is fully acknowledged and explained by the project, bringing me in on the details of the compromise. 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’.
- deleted 5y ago[deleted]
- mdoms 5y agoSometimes 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?
- gfosco 5y agoYou're not the only one. Luckily, we have options.
- dimgl 5y agoThis article is a bit long-winded. I don't reason about React at all like the author of this blogpost. Using React with hooks is dead simple, and getting a React app running takes minutes.
- rezonant 5y agoMinutes to build, years to unspaghettify
- dimgl 5y agoThis is true of all projects that scale uncontrollably
- rezonant 5y agoIt is not true of all projects _to the same degree_. Highly conventional, opinionated systems have a higher learning curve but yield a more consistent result given developers with inconsistent skill levels and investment in architectural design of an application. That is a fancy way to say: React gets new devs productive fast, and so it has tons of them available for work, but that comes at the cost of producing more spaghetti that more experienced devs inevitably have to clean up.
- dimgl 5y ago> Highly conventional, opinionated systems have a higher learning curve but yield a more consistent result given developers with inconsistent skill levels This is highly debatable. A lot of conventional and opinionated systems have lots of under-the-hood magic that make it hard to reason about, even for experienced developers.
- eru 5y ago> 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”. 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. https://en.wikipedia.org/wiki/Applicative_functor https://en.wikipedia.org/wiki/Applicative_functor
- paldepind2 5y agoI agree. If anything in math can be said to mean “X is composable” I would say that that statement is “X is the arrows of a category”.
- eru 5y agoMaybe. 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.
- zamfi 5y ago*aphorism
- akdor1154 5y agoMy first boss had an often repeated mantra in code reviews: "akdor, don't fight the damn language!" 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.
- eyelidlessness 5y agoWhich is all good and well, but the language is fighting the language. If you don’t believe me, just look at active tc39 proposals. JavaScript is increasingly becoming a compilation target and writing it idiomatically is increasingly a mishmash of preferences.
- thaumasiotes 5y ago> JavaScript is increasingly becoming a compilation target 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.
- leecommamichael 5y agoI hope you mean this in the manner that usually we _don't_ target Intel chips, and instead target the x86_64 ISA. Sure, it's possible to target JS, but in the general-case it's wildly inefficient, and requires mental gymnastics compared to something like WASM. This is likely why people are bothering with WASM at all, rather than accepting JS as "the browser's machine code."
- dbbk 5y agoWouldn't it make more sense to have WebAssembly as a compilation target?
- jaredcwhite 5y agoReact 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 opposite direction. After having to use them in a large React codebase for years now, I still can't stand them. 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.
- tristanMatthias 5y agoI love lit. Smoothest developer experience with a very small learning curve. Curious, what do you use for state management across WC’s?
- orangepanda 5y ago> After having to use [hooks] in a large React codebase for years now, I still can't stand them. 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.
- rat9988 5y agoWhat would classes have brought to the table with syntactical sugar? Especially that themselves are syntatical sugar on functions.
- KronisLV 5y ago> After having to use them in a large React codebase for years now, I still can't stand them. I actually feel much the same way and have vented about some of my frustrations before: https://blog.kronis.dev/everything%20is%20broken/modern-react-is-broken 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.
- jackblemming 5y agoThe 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. Nice article though.
- leecommamichael 5y agoI 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.
- captainmuon 5y agoI 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. 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?
- pennaMan 5y agoThe old class based way of doing things was a slap in the face for anyone expecting a "reactive functional ui library" and it put me off react until they did the proper hooks implementation.
- onion2k 5y agoThe old class based way of doing things was much more explicit. 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-from-classes/ 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/ 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.
- alserio 5y agoThere are a lot of similar feeling alternatives beyond react. For example SolidJs is jsx based and ditches also the reconciler (modulo lists). It has a lot of cleverness in what it does with that jsx, but the result is way more straight forward and composable.
- freindk 5y agoReact 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. 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.
- dgb23 5y agoI don’t argue with the performance and bloat issues, but the state management problems you describe are not react specific at all. In fact react helps you to manage these things nicely.
- stephen 5y agoReally interesting to see the various examples/incarnations the author went through to illustrate his point. Very well done. 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?...