Settings

Theme

Hooks: React’s Do-Notation

devanshj.me

84 points by graup 4 years ago · 79 comments (78 loaded)

Reader

jaredcwhite 4 years ago

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 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.

  • orangepanda 4 years 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.

  • KronisLV 4 years 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-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.

  • rat9988 4 years ago

    What would classes have brought to the table with syntactical sugar? Especially that themselves are syntatical sugar on functions.

  • tristanMatthias 4 years ago

    I love lit. Smoothest developer experience with a very small learning curve.

    Curious, what do you use for state management across WC’s?

akdor1154 4 years ago

My 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 4 years ago

    Which 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.

    • saurik 4 years ago

      God I wish TC39 would treat JavaScript as a compilation target, but they do seem to keep adding new complex features for end-developers (which, as you say, are mixing up what it even means to code in the language: the class-oriented stuff in a prototype-based language is causing a whack-a-mole of syntax complexity to regain what they lost, such as with decorators) that of course every implementation will have to support (which alone should be a reason to try to only add features and behaviors to the browser that absolutely could not possibly be simulated effectively in code: browsers should be easy for a few people to implement, like a JVM, with most users using nothing but JS and canvas, generated as a compilation target... but, instead they are spiraling behemoths of complexity with ever-burgeoning CSS and HTML features that even Microsoft had to finally tap out of bothering to maintain).

    • thaumasiotes 4 years 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 4 years ago

        I 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 4 years ago

        Wouldn't it make more sense to have WebAssembly as a compilation target?

    • acidbaseextract 4 years ago

      The progressive C++ification of JS is scary and exciting in equal measure.

  • IshKebab 4 years ago

    Reasonable advice to an extent, but sometimes the language is crap and you should fight it. If everyone had followed your boss's advice we'd still all be using `var` and `for in`.

  • wutXthreex3 4 years ago

    > in the user's browser in a scripting language at runtime

    ok, but it will just get compiled down to machine code?

freindk 4 years ago

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.

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 4 years ago

    I 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.

captainmuon 4 years ago

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.

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?

  • onion2k 4 years ago

    The 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...

    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.

  • lewispollard 4 years ago

    The way hooks are handled when invoked in functional components is magic, as all stateful things in react are, but hooks themselves are very straightforward. Once you've written a custom hook, it's obvious that they're basically just (yes, this is an oversimplification) react components without the render step. You use the React API just like any other component, including using hooks, and then return some piece of state or whatever is needed. Like functional components, they're just functions that run top to bottom when the props change.

  • pennaMan 4 years ago

    The 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.

  • root_axis 4 years ago

    Hooks are more explicit for the uninitiated. Understanding the semantic meaning of when arbitrary method names are invoked throughout the component render cycle requires more investment in react's documentation than intuiting the logic of imperative function calls like "setState" in the body of functional component.

  • alserio 4 years ago

    There 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.

jackblemming 4 years ago

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.

Nice article though.

  • rat9988 4 years ago

    > 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.

    Nicely put, I'll steal it.

  • the_gipsy 4 years ago

    Way to troll everybody

xixixao 4 years ago

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.

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 4 years ago

    Agreed 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/

    [1] https://overreacted.io/why-do-hooks-rely-on-call-order/

  • playpause 4 years ago

    For 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’.

stephen 4 years ago

Really 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?...

bigyikes 4 years ago

The 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 4 years ago

    I 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.

  • westoncb 4 years 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/).

    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 4 years 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 4 years ago

    I 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.

  • eru 4 years ago

    You can have a look at functional reactive programming for ideas.

    The article even links to more material about FRP.

    • jitl 4 years ago

      FRP 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 4 years ago

        Same. Every Rx codebase I’ve seen has been nightmarish to contemplate. I’d rather program in brainfuck.

      • eru 4 years ago

        I 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?

leecommamichael 4 years ago

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.

eru 4 years 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

  • paldepind2 4 years ago

    I 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 4 years ago

      Maybe.

      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.

blindseer 4 years ago

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 :)

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 4 years ago

    I 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 4 years ago

    I had some issues with re-scrolling here on Safari, but not too bad. Probably a minor fix.

    • dwaltrip 4 years ago

      The latter half of the article had some quite bad scrolling problems for me on mobile safari.

  • aaronbrethorst 4 years ago

    I’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.

zamfi 4 years ago

*aphorism

mdoms 4 years ago

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?

  • westoncb 4 years ago

    > do you really need all this to make a UI

    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.

    • makotech221 4 years ago

      Unfortunately for your argument, Gmail was far superior before the big 'app' rewrite.

      • westoncb 4 years ago

        If my argument were that the new version of Gmail was better than the old one, and if the old version being "far superior" had some objective grounding—then you might be on to something.

        But that wasn't my argument at all. Gmail isn't even written with React. And if it were it would still be irrelevant: I'm not arguing "writing a program in React means it will be a good and well-designed program"; I'm saying if you write the same complex program twice, once with built-in browser APIs and once with React, you will on average find that the React implementation is far quicker to write, more concise, fewer bugs, and so on.

        • mdoms 4 years ago

          > if you write the same complex program twice, once with built-in browser APIs and once with React

          That's a false dichotomy.

          • westoncb 4 years ago

            I suppose one might arise if I were claiming those were the only two options. But on a closer re-reading you'll easily find that's not the case.

    • aaronbrethorst 4 years ago

      Nah, disagree. Case in point is Basecamp’s Hey product. Rails monolith. Server side rendered. JavaScript ‘sprinkles’ with Hotwire. https://twitter.com/dhh/status/1275901955995385856?s=21

      • westoncb 4 years ago

        What I can find on "Hey" so far looks extremely basic as far as UI goes.

        And by all means, if what you're building only requires a very basic UI—absolutely go for it.

        My argument is that when you're faced with is an intrinsically difficult UI problem, React (or similar FRP-inspired frameworks which the community has stabilized around) is a huge improvement over the "simpler" past way of doing it. And the only way to really understand that is to try building non-trivial applications with both.

        • aaronbrethorst 4 years ago

          Whatever makes you happy, my dude

          • westoncb 4 years ago

            I mean, am I mistaken? Is the UI more complex than it looks or something? I'm just basing my comment off what I've been able to find through your reference.

            If there is some new system that's a genuine improvement over React and kin I'd love to know about it.

            • Scarblac 4 years ago

              Generally speaking, simple UI sounds like something to aim for. If your UI is "intrinsically difficult", there's a high risk the user will conclude the same thing.

              • westoncb 4 years ago

                Not intrinsically difficult to use, I mean that the problems involved in implementing some ui concept may be intrinsically difficult or not (this can happen even when from the user's perspective the UI is simple/elegant). When they are, then you need appropriate tools.

                And yes absolutely as I’ve said, if it’s possible to for some domain to have a basic ui do that.

              • rat9988 4 years ago

                Usually the difficulty of a user to conceptualize something has nothing to do with the complexity of the code.

  • dimgl 4 years ago

    This 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 4 years ago

      Minutes to build, years to unspaghettify

      • dimgl 4 years ago

        This is true of all projects that scale uncontrollably

        • rezonant 4 years ago

          It 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 4 years 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.

  • etaioinshrdlu 4 years ago

    It took me 2 years to not utterly dislike React. Now I don't love it, but I'm okay with it.

    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.

  • JoeyJoJoJr 4 years ago

    Coming to React, after working with OOP for UI, was pretty mind blowing for me. It is so much better than anything I was working on 5 years ago. React has dramatically improved the landscape as a whole.

    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.

  • ovao 4 years ago

    There isn’t actually a whole lot of “this” in this —- from an API surface viewpoint, most UI frameworks (React included) are fairly minimal sets of abstractions. The benefit is more along the software development axis of homogenization than anything else.

  • Cthulhu_ 4 years ago

    At the moment yes, given that things I'm improving is an old (2012) Dojo application where the closest thing to a component is a function call that concatenates HTML, CSS and JS together. Yes that's JS-in-a-JS-string, it's er, interesting.

    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.

  • Scarblac 4 years ago

    I'd consider that a strange question since these development means the amount of code required is getting less and less each year. React code is really concise now IMO.

  • kimi 4 years ago

    Go have a look at what Elixir is doing with LiveViews ....

    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.

  • gfosco 4 years ago

    You're not the only one. Luckily, we have options.

  • rezonant 4 years ago

    As a frontend developer I can confirm that you do not, in fact, need any of these things to make a UI.

  • dbbk 4 years ago

    As someone working on a project management app, complete with gantt chart functionality... yes.

Keyboard Shortcuts

j
Next item
k
Previous item
o / Enter
Open selected item
?
Show this help
Esc
Close modal / clear selection