[HN Gopher] Hooks Considered Harmful
       ___________________________________________________________________
        
       Hooks Considered Harmful
        
       Author : smtx
       Score  : 197 points
       Date   : 2022-03-21 12:11 UTC (10 hours ago)
        
 (HTM) web link (labs.factorialhr.com)
 (TXT) w3m dump (labs.factorialhr.com)
        
       | [deleted]
        
       | lloydatkinson wrote:
       | I agree with this article, when I'm doing frontend development I
       | much prefer to use Vue - with its options API (I ignore the
       | composition api as it's essentially a clone of reacts hook
       | system).
       | 
       | But when I do any react such as assisting another team using it,
       | I am constantly surprised and reminded just how much of a bad
       | developer experience react hooks are.
       | 
       | The coupling of needing to constantly be aware of the intricacies
       | of rendering while also balancing reactivity and data binding
       | leads to probably the most offensive API I've ever had to use.
       | 
       | When writing useState or useEffect etc the not always required
       | last argument is a damn array or empty array or sometimes an
       | array with several items.
       | 
       | What does not passing an array mean? Well, I'm sure someone could
       | explain at great length the complexity and alleged need for this
       | but anyway: passing nothing as the last argument of useEffect
       | results in an infinite loop.
       | 
       | Passing an empty array means "don't render again until actually
       | needed".
       | 
       | The third option of passing a variable length array results in
       | something else entirely I still don't understand. Every article
       | does it slightly different.
       | 
       | Ultimately what this leads to for me at least is a developer
       | hostile API full of seemingly intentional foot guns. The other
       | day I had the situation where useEffect to call an API resulted
       | in calling the API endlessly and constantly. Then I thought "ah I
       | have seen this before, it means I have to pass an empty array".
       | 
       | It's absolutely infuriating and smells of a bad API design. What
       | happened to the importance of DX (developer experience)?
       | 
       | As I said I'm sure someone could hand wave away the alleged need
       | for this complexity but honestly it simply feels like a bad API.
       | No one will ever convince me that an empty array vs no argument
       | is "functional" because functional code values making side
       | effects and purity deliberate decisions with a focus on clarity.
       | 
       | How do other frameworks let you fetch from an API? Generally a
       | simple assignment and await statement.
        
         | bern4444 wrote:
         | No array means execute the hook on every rerender of the
         | component.
         | 
         | Empty array means execute the hook once on initial render.
         | 
         | Non empty array means execute the hook when the value of
         | anything in the dependency array changes.
         | 
         | These are pretty simple to remember imo.
         | 
         | An alternative design could have been to pass in an object as a
         | second parameter instead of an array with these options details
         | like                 { runOnlyOnInitalRender: true }
         | 
         | Or                 { alwaysExecuteOnRerender: true }
         | 
         | Etc. But that feels far more verbose than the three options we
         | have now.
        
           | lloydatkinson wrote:
           | > These are pretty simple to remember imo.
           | 
           | But why should we? No other reactive framework makes the
           | developer go through this ritual. Even RxJS is clearer to
           | understand.
           | 
           | > Etc. But that feels far more verbose than the three options
           | we have now.
           | 
           | A better designed API would require neither.
        
         | bpicolo wrote:
         | The composition feels a lot closer to the "ratom" concept from
         | the Clojurescript ecosystem (https://github.com/day8/re-
         | frame/blob/master/docs/flow-mecha...) than it does hooks. I
         | don't hate it at all.
        
       | jitl wrote:
       | This really hit home for me:
       | 
       | > I do not exaggerate when I claim that I find a dozen of hooks-
       | related problems every single week while reviewing code.
       | 
       | I also see sooo many issues when reviewing code like leaked event
       | listeners, unstable props causing re-renders, etc. And these
       | issues show up from teammates who otherwise write impeccable and
       | trustworthy PRs in other regards.
       | 
       | I enjoy writing hooks style code, and for me reasoning about
       | lexical scope & closures is second nature. But for many engineers
       | used to OOP, hooks code is the first time they're asked to do
       | this kind of reasoning since leaving university. In OO
       | Java/JavaScript, it's very normal to declare a new class and have
       | the only two scopes be the current function's locals, and class
       | instance members. Hooks code on the other hand can easily reach
       | two or three layers of local closure scopes, each with different
       | lifetimes. I think this is _fun_ and _clever_ , but I also prefer
       | to maintain _boring_ and _simple_ code... I'm worried that hooks
       | ramps up trivial complexity too much in exchange for often-
       | unneeded power.
       | 
       | On the other hand, function components and hooks tend to guide
       | people more toward splitting up their big mega components into
       | smaller parts. At all companies I've worked for, product eng
       | across the stack tends to produce mega objects cause it's easy to
       | just keep adding private methods to do _one more thing_ , and
       | splitting up responsibility of state encapsulation takes some
       | extra reasoning. At least with FC/hooks, the API and linter force
       | you to unroll funky logic or loops into sub-components, since you
       | can't call hook in loop or conditional.
        
         | bilalq wrote:
         | > I also see sooo many issues when reviewing code like leaked
         | event listeners, unstable props causing re-renders, etc. And
         | these issues show up from teammates who otherwise write
         | impeccable and trustworthy PRs in other regards.
         | 
         | I think this is true. However, I don't think I saw less bugs
         | with `component(Did|Will)(Mount|Update|ReceiveProps)`.
         | Lifecycle events are intrinsically about state management, and
         | that has always been the root cause of many bugs. This isn't a
         | React specific problem either. Back in AngularJS 1.2.x days,
         | the $scope and digest cycle was the source of many bugs. In
         | Backbone, people's two-way binding between views and models
         | were the source of many bugs.
         | 
         | Are React hooks complex? Yes. I don't think they're worse than
         | what existed before though.
        
           | totaldex wrote:
           | I don't entirely agree - the old React way was verbose and
           | explicit about when events are executed. This allowed
           | developers to more concretely reason about logical workflows
           | in applications.
           | 
           | With hooks, we traded verbosity for a single interface that
           | does it all (assuming you know how to hook up your
           | dependencies correctly, or compose helper hooks to manage
           | state comparisons). Hooks allow you to do mostly anything
           | lifecycle methods did, but they're a lot trickier to reason
           | with, review, and develop.
           | 
           | This all goes away if all your developers are functional
           | maestros - in practice, it's lead to buggier code across our
           | various frontends.
        
             | bilalq wrote:
             | My experience has been pretty different. With lifecycle
             | methods, you had the implementation details of a single
             | concern split across the constructor, multiple lifecycle
             | handlers, and sometimes even the render function when refs
             | were involved. Hooks can express full concerns in a
             | reusable way. This is a valuable abstraction that
             | previously required complex higher order components to do.
             | 
             | People would also constantly get tripped up over
             | `this.props` and `this.state` when it came to computed
             | state values. Now a simple `useMemo` simplifies and
             | expresses that intent way better than setting something
             | conditionally in the constructor and doing a bunch of
             | conditional checks on componentWillUpdate before calling
             | this.setState again.
             | 
             | Edit:
             | 
             | Oh, and the improvements with Redux are life-changing. The
             | `useTypedSelector` UX is so much better than writing a
             | mapDispatchToProps, mapStateToProps, and then having a
             | bunch of merging ceremony there.
        
               | totaldex wrote:
               | I'd chalk it up to a difference of opinion then - I like
               | simplicity, but I'd take verbose clarity over it any day.
               | Having to explain to newer engineers the nuances of hooks
               | (and the permutations required to wrangle them in) is
               | harder for me than saying "this exact lifecycle method is
               | what you're looking for, take a look at its
               | documentation".
        
         | [deleted]
        
         | thr0wawayf00 wrote:
         | > I think this is fun and clever, but I also prefer to maintain
         | boring and simple code... I'm worried that hooks ramps up
         | trivial complexity too much in exchange for often-unneeded
         | power.
         | 
         | I've been in and out of the React world for the last 5 years,
         | and this statement hit hard for me. Between the major shifts in
         | best practices, the abstractions on top abstractions and
         | constantly tripping up on the slight syntax differences between
         | JSX and markup, so many commercial React apps I've worked on
         | make me want to pull my hair out, and not just because of
         | hooks.
         | 
         | In hindsight, the virtual DOM hype has not lived up to
         | expectations, and I find newer frameworks like Svelte to be so
         | much easier to work with. With the amount of React code running
         | today, it's hard to see a future without it, but I'm so ready
         | for it to be supplanted by something simpler.
        
           | djbusby wrote:
           | I had bad feelings about React when I first saw it. Then I
           | saw RiotJS and that just clicked for me. I like Svelte too -
           | haven't put it in production tho
        
           | bpicolo wrote:
           | I have to say, I've never felt this in the Vue ecosystem.
           | It's relative simplicity in state management, and slower
           | relative paradigm shifts have been pleasant. The 2->3 shift
           | wasn't perfectly done (and it did split the state management
           | stories), but overall I've felt like I "kept up" fairly
           | easily, compared to React.
           | 
           | And that's while writing a lot more React than I did Vue.
        
           | jitl wrote:
           | There's too much framework in Svelte for me; I don't like
           | feeling so estranged from "regular" code. I think SolidJS is
           | more appealing - although it might be _even more clever_ than
           | React...
        
             | thr0wawayf00 wrote:
             | To each their own, I guess.
             | 
             | I haven't built anything with SolidJS, but it's not my
             | style based on the docs. It is interesting that you bemoan
             | being so far from "regular" code in Svelte when it's
             | basically a minimal superset of HTML, as opposed to Solid,
             | which uses JSX (which feels very far from "regular code" to
             | me). Also given the fact that Svelte is a compiler that
             | emits vanilla JS, I have a lot of more control over
             | performance, compile-time checks for things like a11y,
             | unused deps, etc. I'd argue that svelte source code looks a
             | whole lot more like it's output than SolidJS does, but
             | that't just me.
             | 
             | It's easily the best overall front end developer experience
             | I've seen, but I've only built smaller projects with it so
             | far.
        
             | com2kid wrote:
             | Svelte feels very minimal to me. It is regular JS where I
             | can say "shove the value of this variable onto the web page
             | and rerender that component when the value changes" but
             | saying all of that is just $:
        
             | danielvaughn wrote:
             | Interesting - I began using Svelte after years of using
             | React, and I found it to be far less framework-y. I feel
             | much more connected to what's actually happening, but I
             | also haven't built out a very complex application with it
             | yet. Is it the weird $: reactive model that you don't like?
             | That was the weirdest part for me, even if it's supposed to
             | be "normal" javascript.
        
         | tshaddox wrote:
         | > I do not exaggerate when I claim that I find a dozen of
         | hooks-related problems every single week while reviewing code.
         | 
         | I'm curious if the author sees dozens of problems every week in
         | code review due to, say, unexpected null/undefined values,
         | mistyping variable or attribute names, using the wrong number
         | of equal signs, failing to catch errors and handle rejected
         | promises, etc.
        
           | acidbaseextract wrote:
           | I'll give you my answers to your questions as someone writing
           | production JS/TS. Answering your questions is precisely
           | illustrative of why hooks issues are hard to catch.
           | 
           |  _unexpected null /undefined values_
           | 
           | This is an issue and has led to bugs. Switching to TS and
           | making the compiler flag them fixed it.
           | 
           |  _mistyping variable or attribute names_
           | 
           | Not an issue. Code obviously breaks if you have incorrect
           | names.
           | 
           |  _using the wrong number of equal signs_
           | 
           | This is an issue but causes few bugs. Linting generally
           | catches it, though cute boolean punning still bites us.
           | 
           |  _failing to catch errors and handle rejected promises, etc._
           | 
           | This is an issue and has led to bugs.
           | 
           | Pretty much anything where there's some implicit details that
           | the compiler or linters can't reason about programmers find a
           | way to get wrong. One thing I like about the hooks linter
           | setup is that what it encourages you to do by default will
           | prevent most bugs, only lead to potential performance issues,
           | unnecessary rerenders, unnecessary refetches.
        
             | rileyphone wrote:
             | > Pretty much anything where there's some implicit details
             | that the compiler or linters can't reason about programmers
             | find a way to get wrong.
             | 
             | touching on an immortal truth!
        
             | bilalq wrote:
             | >> failing to catch errors and handle rejected promises,
             | etc.
             | 
             | > This is an issue and has led to bugs.
             | 
             | > Pretty much anything where there's some implicit details
             | that the compiler or linters can't reason about programmers
             | find a way to get wrong. One thing I like about the hooks
             | linter setup is that what it encourages you to do by
             | default will prevent most bugs, only lead to potential
             | performance issues, unnecessary rerenders, unnecessary
             | refetches.
             | 
             | This is something that can be mitigated via the no-
             | floating-promises linter rule if you're using
             | TypeScript[0]. For the cases where you actually want to
             | just swallow errors, you can just add a `.catch(noop)`.
             | This makes such situations explicit. You can get even
             | stricter with the no-misued-promises rule[1].
             | 
             | [0]: https://github.com/typescript-eslint/typescript-
             | eslint/blob/...
             | 
             | [1]: https://github.com/typescript-eslint/typescript-
             | eslint/blob/...
        
         | bocytron wrote:
         | Can anyone here recommend a blog post that go through the most
         | common mistakes when using hooks?
        
           | WorldMaker wrote:
           | The article here ("Hooks Considered Harmful") actually
           | probably would be better called "Most Common Mistakes When
           | Using Hooks". It seems to cover the most common ones I'm
           | aware of.
        
           | Jcampuzano2 wrote:
           | Don't have a blog post, but the majority of issues you will
           | find are with dependencies arrays for
           | useMemo/useCallback/useEffect.
           | 
           | Most common I see:
           | 
           | 1: Missing dependencies (for folks who don't use the linting
           | rule)
           | 
           | 2: Not understanding reacts async state model: example being
           | expecting state to have been updated immediately after
           | calling setState inside an effect
           | 
           | 3: Not understanding closures: an example is creating an
           | interval in an effect that uses and updates a useState value,
           | but being confused why it isn't updated when the interval
           | repeatedly fires.
           | 
           | I agree wholeheartedly with the author. Hooks are powerful
           | and even I was super excited to start using them when they
           | released. But now I think the hooks paradigm leads to even
           | worse and bug-ridden code than what we had before.
           | 
           | The docs are pretty good, but don't seem to cover very well
           | that when using hooks you really need to know JS well, which
           | includes equality in JS, closures, etc otherwise you will be
           | guaranteed to shoot yourself in the foot.
           | 
           | There is so much more business logic mixed in with component
           | code at seemingly random in projects as well such that it
           | makes finding where "stuff" happens even more difficult than
           | before.
        
       | ChrisMarshallNY wrote:
       | _> The argument usually follows that state is evil, hence object-
       | orientation must be avoided._
       | 
       | I've found that it's _really, really difficult_ to design _good_
       | UI, without state. The  "solution" that many UI systems use, is
       | leaving the state in the view, but that often results in a pretty
       | degraded user experience. Sometimes, the state needs to live in
       | the model, as it may interact with a whole gaggle of views, or
       | apply sets of state.
       | 
       | So the "solution" there, is to tie the views together, or save
       | the state in little "statelets," connected to views; resulting in
       | an ... _interesting_ ... design.
       | 
       | I've come to learn that "hard and fast" rules are a mistake.
       | 
       | It's been my experience, that I often need to approach a solution
       | in a hybrid manner, and really appreciate new techniques and
       | technologies.
       | 
       | But sometimes, we need to stick with the classics.
        
       | grayrest wrote:
       | I have not adopted hooks and have argued against it for others on
       | the team. My primary objection is that the paradigm is not clean.
       | 
       | Class based components are fairly straightforward. The entire
       | render function is run on every change.
       | 
       | Solid/Svelte are fairly straightforward. The component is run
       | once and then only the reactive parts change.
       | 
       | Hooks run the function on every change but there are islands of
       | non-running code inside the constantly rendering body where only
       | run when their dependencies change. This strikes me as an
       | obviously intermediate solution and I don't want to spend time
       | porting/developing something that's going to be obsoleted in a
       | year or two.
       | 
       | The next framework generation is underway and the time for early
       | adopters to move on is in the next year or so. The main reason to
       | hold off is that the handling of SSR/hydration/etc is in flux and
       | I believe the primary benefit of the upcoming generation is going
       | to be ability to avoid shipping most component code over the
       | wire.
       | 
       | I have a lot of respect/love for React. I've seen quite a bit of
       | criticism that the vdom idea is inherently inefficient but the
       | important thing about it was that it was _reliable_. Lots of JS
       | used to do all sorts of crazy stuff with the dom underneath you
       | but React has basically cleared the field of most of that which
       | allows more fine grained approaches to work consistently.
        
       | softfalcon wrote:
       | We use a thing called Relay (https://relay.dev/) to manage our
       | querying, mutating, and state management.
       | 
       | When you query data, it is cached locally in a shared data store
       | that will automatically trigger re-renders of components that
       | take in the data types as a dependency.
       | 
       | You don't have to write an update to the data, you query it and
       | Relay updates all the underlying models in the local state cache.
       | 
       | The data types are correlated to your components via GraphQL
       | fragments which allows Relay to know what and when to update.
       | 
       | Because most of the values you are using in your component are
       | already stateful, you end up using A LOT less hooks since most
       | state updates will trigger and happen for you.
       | 
       | It's incredibly powerful once you get going with it.
       | 
       | The downside is getting it to work properly with the type
       | generation. It can be rather finicky and Facebook likes to
       | rewrite the API out from under you every couple years.
        
       | TameAntelope wrote:
       | > Most of those issues never manifest to the end-user, but
       | incorrect code that is not a bug today will, eventually.
       | 
       | No... what? Could not disagree more with this sentiment; this is
       | the "for the love of the game" kind of stuff that completely
       | loses focus on _why_ we write code in the first place; to make
       | money. Very, very few people have the resources to care about
       | this level of problem, and far more often people who _don 't_
       | have the resources end up spending them on useless "improvements"
       | like what's discussed in the article, rather than building things
       | that users can use (or that make it easier to build things users
       | can use).
       | 
       | Hooks are surely flawed, in the same sense that literally
       | everything in software is flawed. The point is not to select a
       | way of writing code that doesn't have flaws; the point is to
       | select a way of writing code that has flaws you can live with.
       | 
       | Hooks have flaws, but hooks also have benefits that make writing
       | software meaningfully easier. Losing sight of that is a great way
       | to write an article that complains about problems that never
       | impact the user's experience.
       | 
       | It's just such a big red flag when someone talks about "incorrect
       | code" that doesn't impact the user in any way. Huge, gigantic
       | waving red flag.
        
         | masylum wrote:
         | Maybe I expressed it incorrectly. When the code is incorrect,
         | it fails in the future. So the user will notice it. This
         | happens when your code relies on some context to work (for
         | instance, if it relies on how the data structure was allocated
         | somewhere up the component hierarchy). When the context
         | changes, then that piece of code starts failing, therefore
         | impacting users.
        
           | TameAntelope wrote:
           | But now it just sounds like you're saying, "When the code
           | changes, the code changes." which feels obvious?
           | 
           | Of course when you change some of the code without changing
           | other parts that rely on the original code, it will break.
           | I'm not sure why that's unique to hooks.
        
       | LAC-Tech wrote:
       | I think people need to wake up to the fact that react encourages
       | bad architecture.
       | 
       | You can tell me react is a big brained functional library all you
       | want. Fact is you're putting business logic and mutable state
       | inside your functions from props -> jsx. The fact that setState
       | is a 'hook' doesn't change the fact you're setting state.
       | 
       | Every react code base I've come across looks exactly like what
       | they told us not to do in the WinForms and Java Swing days - code
       | behind.
        
       | Cthulhu_ wrote:
       | Alright so, reading through all this I think I can summarize it:
       | 
       | - Hooks are tricky because you need to pass them an array of
       | dependencies, which is manual housekeeping
       | 
       | - You shouldn't pass anything but primitives to a hook's
       | dependency array if at all possible
       | 
       | What is the alternative? Just pay attention to the two above, or
       | go back to class based components? Or will there be a React-
       | flavored JS/TS (like JSX / TSX) that has different closure
       | mechanics?
        
         | [deleted]
        
         | jcelerier wrote:
         | A sane solution for exactly this problem has existed for a
         | decade now: https://www.qt.io/product/qt6/qml-
         | book/ch04-qmlstart-qml-syn...
        
         | spiffytech wrote:
         | > What is the alternative? Just pay attention to the two above,
         | or go back to class based components?
         | 
         | React may have limited options with this design, but other
         | frameworks have taken other approaches to the problem:
         | 
         | Vue/Svelte/MobX only run the setup code for hooks (or closest
         | equivalent) once. Derived values and effects are automatically
         | run without specifying dependencies - the tools detect what an
         | effect reads while it runs, and track dependencies for you.
         | Since effects are only set up once, closure values from the
         | setup scope don't expire/disappear, so they can't go stale in
         | the same way as in React (caveat destructuring). I think Solid
         | is in this camp too, but I haven't used it.
         | 
         | Frameworks like Mithril and Forgo ditch state tracking and
         | effects entirely. You explicitly tell the framework when to
         | rerender etc., and everything else is just you calling
         | functions and assigning variables without the framework's
         | supervision.
         | 
         | Crank.js extends the explicit-rerender idea by using generators
         | for components. This preserves the "a component is just a
         | function" feature from React, but avoids the hooks pitfalls by
         | only executing a function once.
         | 
         | Hyperapp doesn't have the notion of components at all, so you
         | can't have component-local state. The framework reruns all view
         | code at once, passing the current global state. You can
         | approximate components by writing functions that slice and dice
         | state and divide up the work, but that's transparent to the
         | framework, and there's no place to store state besides the
         | global state.
         | 
         | These all have trade-offs. They may require more complex
         | runtimes / toolchains, or simply shift around the burden on the
         | programmer (what's easy/hard, what kind of bugs will be
         | common).
         | 
         | I'd love to see more approaches in this space. Not all trade-
         | offs are right for all situations, and I'd like to see more
         | ideas that meaningfully change the development experience,
         | rather than "if you squint it's basically the same thing"
         | ideas.
        
           | jsmith45 wrote:
           | > I think Solid is in this camp too, but I haven't used it.
           | 
           | Correct. Solid is all about signals (reactive values). When
           | you run any effect (rendering updates are effects created
           | behind the scenes for you), it will get run once immediately,
           | tracking which signals where called. Then it will subscribe
           | to those signals to re-run the effect on change, and it will
           | resubscribe to newly called signals, and unsubscribe from no-
           | longer called signals.
           | 
           | I believe that it is roughly equivalent to Vue's reactive
           | api, except that rather than using a proxy or setters to
           | allow object mutation to trigger effects or re-render, it
           | uses separate update functions, more like react hooks do.
        
         | WorldMaker wrote:
         | I think most specifically the call out in the article is: the
         | Hooks that React provides are extremely "low-level" (and
         | intentionally so) and while you can do everything with just raw
         | low-level hooks, consider returning to higher level hooks. The
         | article provides some simple (Typescript type-safe examples) of
         | higher level hooks. It also recommends that
         | Redux/MobX/Relay/etc are still very useful higher level tools,
         | even or especially in hook-based components in React.
        
         | jitl wrote:
         | Jetpack Compose solved this problem in two ways:
         | 
         | 1. State you read in a ~hook~ aka Composable is/should be an
         | instance of a special kind of observable object. You can create
         | your own subclasses or just use the standard state container
         | much like useState. This means the system has more information
         | to produce minimum subscriptions/reactions at runtime.
         | 
         | 2. The system used crazy compiler transforms to turn functions
         | marked @Composable into reactive scoped hooks/components. Using
         | the compiler eliminates a lot of error-prone boilerplate and
         | bookkeeping code otherwise required for these kinds of systems
         | in standard OO languages without monad+do-notation by adding a
         | sublanguage "manually".
         | 
         | Downside to the Compose model is that it's even more
         | mindbending to understand. Developers are encouraged to
         | surrender to the magic. I've yet to read/write enough Compose
         | code to understand the cost benefit analysis yet.
        
         | colejohnson66 wrote:
         | > ...or go back to class based components?
         | 
         | Was there anything _wrong_ with class components? It 's what I
         | learned half a decade ago, and the idea of a "state" object
         | made so much sense. Now, with hooks and whatnot, it seems like
         | React is trying to be "functional" without actually being so.
        
           | mejutoco wrote:
           | I would say mixing state and behaviour was the main problem
           | with class components (and oop), although it might be a
           | better problem to have than all the approaches supported
           | simultaneously (some code with class components, some hook
           | heavy, etc)
        
           | adamddev1 wrote:
           | I also liked/like the idea of one state object. With hooks
           | now they really discourage packing everything into one state
           | object and you either have to use a bunch of different
           | `useState` statements for your different variables or pack a
           | big state object in one `useState` but then you face big
           | performance issues with needless re-renders when you only
           | change one element of the state object.
        
             | andsbf wrote:
             | Take a look at useReducer(no 3rd package, part of the React
             | api)
        
           | codeflo wrote:
           | I'll prefix by saying I agree with a lot of the criticisms,
           | and I've experienced first hand how hard to explain and error
           | prone hooks are. But for this post, let me defend the
           | concept.
           | 
           | The idea, in a very rough nutshell, is to allow separating
           | behavior and presentation.
           | 
           | Hooks are the reusable unit of behavior. You compose hooks
           | into more complex hooks that might implement loading and
           | saving data from/to the server, for example.
           | 
           | Then you can use this hook with different components, or use
           | the same component in a different context with a different
           | data source. This can be very powerful if used well.
           | 
           | But as I said at the beginning, hooks are unfortunately also
           | very difficult to get right.
        
             | HeyImAlex wrote:
             | To expand on this, many of the things that hooks now make
             | easy were bespoke per-class implementations with details
             | that leaked into every lifecycle hook. Think of how you'd
             | write a chain of useMemo calls in a class component to see
             | just how bad it was.
        
           | mostlylurks wrote:
           | With class components, lifecycle management is trickier to
           | encapsulate and reuse since you have to split such things up
           | and scatter them across several different methods or wrap the
           | whole component in a HOC, and in general class components
           | require more boilerplate. Thus you'll usually end up with
           | code that's just a bit more spaghetty than with function
           | components. With hooks, you can at least encapsulate some
           | specific behavior behind exactly one function call without
           | jumping through any extra hoops.
        
         | mejutoco wrote:
         | Unless I am missing something, functional components and
         | centralized state (a la elm https://guide.elm-
         | lang.org/architecture/) are enough. Bringing global state
         | (Context called from anywhere) can make it less clear.
        
         | codeflo wrote:
         | It's often not even that much manual housekeeping. If you
         | follow the second advice, then ESLint will do a fine job of
         | telling you exactly what's missing or superfluous.
         | 
         | (I argue that ESLint is almost required when working with
         | React. You can turn off its weirder other rules if they become
         | an annoyance, but the hook rules are golden.)
         | 
         | No, I think the problem is the combination of closures,
         | mutability and identity. Very few other things in programming
         | punish you that harshly (and subtly) if you don't have a
         | crystal clear understanding of all three concepts.
        
           | latchkey wrote:
           | ESLint is fantastic, but what would be more golden is unit
           | tests failing or code not compiling. It is almost like hooks
           | need to be written in a meta language like JSX so that they
           | can be compiled and thus failed when written poorly.
        
       | mejutoco wrote:
       | IMO hooks are a DSL implemented in js. React went from class to
       | function components to hooks as preferred practices. In the
       | process, the previous was not cleaned up (until very recently,
       | maybe still, you needed a class component to catch exceptions in
       | a component). It feels like change for changes sake, were only
       | new features are explored without making the whole consistent.
       | 
       | I like React a lot. A simple conceptual model, like elm or other
       | frameworks mentioned here, would work better that this constant
       | change.
        
         | dragonwriter wrote:
         | > React went from class to function components to hooks as
         | preferred practices
         | 
         | Hooks work with function components, and are how component
         | state and the equivalent of lifecycle methods are implemented
         | in function components.
         | 
         | For a while with function components, higher-order components
         | were a common complementary approach to hooks, which have
         | themselves largely had their function subsumed by hooks, so you
         | could maybe describe the trend as:
         | 
         | class components => function components + hooks + HOC =>
         | function components + hooks.
        
           | mejutoco wrote:
           | That sounds right. In my own anecdotal experience I see a
           | much bigger push to use hooks for everything, where before
           | only useEffect and a few others would be expected.
           | 
           | I wish old methods would be deprecated and all features made
           | available in function components.
        
         | kazinator wrote:
         | We know from Lisp that, for instance, you can write a while
         | loop like this.                 (defmacro while (condition
         | &rest body)         `(let ((cond-fun (lambda () ,condition))
         | (body-fun (lambda () ,@body))            (while-macro-run-time-
         | function cond-fun body-fun)))
         | 
         | Then we have a run-time support function:
         | (defun while-macro-run-time-function (cond-function body-
         | function)         (loop while (funcall cond-function)
         | do (funcall body-function)))
         | 
         | Closures allow macros to parcel off expressions or bodies of
         | expressions into functions, so that control structures can then
         | be made "remote": put into a function.
         | 
         | This has the benefit of keeping expansions small. Another
         | benefit is that since the core logic is in the run-time
         | function(s), those can be updated to fix something without
         | having to recompile the macro invocations.
         | 
         | Somehow, the sky doesn't fall in Lisp land; we don't need
         | articles like, OMG I learned about this in 2018 and it's so
         | dangerous.
        
           | mejutoco wrote:
           | I think probably you intended to reply to some other user,
           | unless I am missing sth.
           | 
           | I see a difference with hooks, where you need a linter to
           | verify that you used them as intended: they must start with
           | useSomething and be called in the top level. As opposed to
           | use native language features.
        
       | kaycebasques wrote:
       | I am building my first non-trivial Next.js app now. It definitely
       | took a couple days to get a simple "fetch data from a third party
       | and render" use case working. And even now I'm not sure if I'm
       | holding it right.
        
         | Toine wrote:
         | It took you a few days to learn a new big & powerful React
         | framework, and your experience apparently was bad, I'm ok with
         | that.
         | 
         | But, sorry : what does that have anything to do with hooks ?
        
           | kaycebasques wrote:
           | Hooks is the key architectural concept for implementing the
           | use case I described!
           | 
           | The problems that this comment described are exactly where I
           | shot myself in the foot with hooks:
           | https://news.ycombinator.com/item?id=30754873
        
         | chrisco255 wrote:
         | No judgment, but what particular aspect of Next.js or React
         | hindered you? How much JavaScript experience did you have
         | before undertaking the task?
        
           | kaycebasques wrote:
           | I was on Google's Web DevRel team for 6 years. Owned all the
           | Chrome DevTools and Lighthouse docs for 4 years and led
           | content strategy for https://web.dev and
           | https://developer.chrome.com for 2 years. Lots of experience
           | building small applications in vanilla HTML/CSS/JS and I
           | build toy apps in whatever frameworks are currently popular.
           | I'm a technical writer by trade so I usually don't have time
           | / motivation / business rationale to dive into a particular
           | framework to the point of mastery but definitely am not a
           | novice either!
           | 
           | This comment [1] describes exactly how I shot myself in the
           | foot w/ hooks. In my head this is how React talks to me now:
           | "Tell me your dependencies. NO NOT THAT KIND OF DEPENDECY!!"
           | 
           | [1] https://news.ycombinator.com/item?id=30754873
        
         | izzygonzalez wrote:
         | I had a Next.js project due for an interview and it basically
         | took my entire weekend, even with a working prototype in
         | another framework. The interviewer later on told me that the
         | assignment was his way of experimenting to find out if the
         | company should switch stacks. I told him it was the worst
         | experience I've ever had coding.
        
       | kazinator wrote:
       | I'm finding that programming language articles by JavaScript
       | developers are a lot like Gang-of-Four Design Patterns in the
       | following way.
       | 
       | When you translate them to Lisp, they either disappear entirely
       | or greatly simplified.
        
       | dgb23 wrote:
       | useEffect is a bit tricky but powerful. The issues mentioned in
       | the article are things you can avoid by reading the official docs
       | a bit carefully and playing around for a few minutes.
       | 
       | The other issues mentioned are JavaScript specific (not
       | React/hooks specific), currently there are libraries that can
       | help (or using a compile to JS language that has proper equality
       | semantics). In the future there might be an implementation of the
       | proposed immutable "Record" and "Tuple" types that will have data
       | literal syntax and all the properties that one wants when writing
       | FRP style UI code.
        
       | ncfausti wrote:
       | I agree. Hooks are an awful hack / offer poor user (developer)
       | experience imho.
       | 
       | Whenever you need to have warnings and rules for using things
       | (that require linter verification to make sure developers aren't
       | shooting themselves in the foot with common/regular usage) it's
       | an anti-pattern.
       | 
       | They are far too easy to mess up, especially for something that
       | is meant to be a fundamental part of the library.
        
       | wishawa wrote:
       | I created a tiny library to make hooks out of classes a while
       | ago. Just posted on Show HN after seeing this.
       | 
       | https://news.ycombinator.com/item?id=30760696
        
       | inglor wrote:
       | I work in an infra team in a large codebase for Microsoft. We are
       | the people who do the sort of bug-analysis to figure out what
       | technologies work well and what needs improvements.
       | 
       | Hooks are a constant area of struggle and people make tons of
       | mistakes (forgetting to cleanup a useEffect, useState closures,
       | and needless useCallback are the top 3) with it.
       | 
       | I dare say that if we didn't have MobX things would have been
       | much much worse.
       | 
       | The annoying bit is that other frameworks like Vue or Solid have
       | MobX like Reactivity baked in which makes things much much
       | simpler.
        
         | erokar wrote:
         | For me it seems React has made some unfortunate choices in an
         | attempt to go FP in a language that does not afford it very
         | well. Instead of embracing JS and working in alignment with its
         | limitations/offerings, the React team has introduced elaborate
         | workarounds that gives a whiff of FP but introduces indirection
         | and verbosity and does not solve basic problems of state (i.e.
         | the example from the blog post with classes vs. closures).
         | 
         | I prefer frameworks like e.g. Svelte that works with what JS
         | affords. If you want to go full FP Elm or ClojureScript will
         | give you more value for your money than React imo.
        
           | WorldMaker wrote:
           | FP is a wide spectrum and JS can do some FP well. I'd argue
           | the issue with what React is attempting with hooks is not
           | that it is FP, but it is an attempt at particular "higher-
           | kinded" monads when JS today mostly just has basic support
           | for simple kinded monads (Promises/thenables and async/await
           | syntax). The types of monads that hooks want to be are
           | advanced and require a bunch of extensions even in "big FP"
           | languages like Haskell. (In theory, if the biggest concern
           | was no do-notation in JS, you could approximate it with
           | async/await syntax. The mental model of async/await might not
           | make sense to what you were doing with it, but the dualism
           | with do-notation should be valid. What Hooks are trying to be
           | needs things like GADTs and other extensions in Haskell from
           | my understanding.)
           | 
           | To me, I still think Hooks are useful for making that big
           | swing attempt in JS despite needing so many crutches like
           | hard education spikes in the learning curve and so many
           | linters, but the problem with Hooks is definitely not that
           | they are "FP" but that they are advanced FP that even FP
           | still hasn't figured out all the bugs.
        
         | purplerabbit wrote:
         | I only wish Mobx's API was terser and more opinionated. Hard to
         | derive a good Mobx pattern from their bloated API (>100
         | exports)
         | 
         | I've created a wrapper library which is basically a more-
         | opinionated, higher-level version of Mobx if anyone is
         | interested: https://www.npmjs.com/package/r2v
        
         | softfalcon wrote:
         | MobX from a quick Google seems to resemble the old Knockout.js
         | 
         | We use Relay to define our state management, trigger global
         | updates across many components, and also query/mutate our data.
         | 
         | Does MobX have a way to do the global state management that
         | Relay does?
         | 
         | I'm curious because after using Relay, I've found most other
         | types of state management to feel extremely heavy with boiler
         | plate code.
         | 
         | Thoughts?
        
           | brundolf wrote:
           | One of the great things about MobX is that it works on plain
           | data and plain operations on that data [0], not just the
           | contents of a sanctioned Store, which means it has no
           | opinions about where you put your data. Local objects
           | captured by closures? Cool. Classes? Great. Mutating
           | arguments? Go for it. Global singleton? Have fun.
           | 
           | [0] Technically, it wraps all the different data structures
           | in Proxies etc as soon as they get assigned into an
           | observable structure. But the goal is for you to never have
           | to think about them as anything other than plain data, and
           | that abstraction very rarely leaks.
        
         | jitl wrote:
         | How does Mobx help your code work better? Does it specifically
         | salve hooks wounds, or just because you don't need to manually
         | manage subscriptions in a component?
         | 
         | I've looked at Mobx and am concerned about mutation. I would
         | like a magically reactive state container that always returns
         | immutable views of the state for local usage. Maybe I should
         | stop worrying and switch everything to Mobx.
        
           | cloverich wrote:
           | Because you don't need to manage subscriptions, there's a
           | whole class of hooks you don't need to write in the first
           | place. Then on top of that, you get extremely good
           | performance. Having to rarely think about hooks and basically
           | never think about performance, for me, free's up tons of
           | cognitive space to focus on the actual implementation of the
           | UI. Its not a perfect system, but I haven't found another I'd
           | rather use.
        
         | eatonphil wrote:
         | If Solid or Svelte had a way to import React components (for
         | 3rd party code) and a documented migration path for 1st party
         | code I'd be interested in switching.
        
         | holler wrote:
         | I was curious and indeed confirmed that ember.js/glimmer.js
         | @tracked properties were inspired by MobX.
         | 
         | From /u/wycats "The rule in Octane is: "mark any reactive
         | property as @tracked, and use normal JavaScript for derived
         | properties". That's pretty cool!"
         | 
         | https://news.ycombinator.com/item?id=21848929
        
         | brundolf wrote:
         | +1 for MobX, can't recommend it enough
         | 
         | [also a quick shill for the general-purpose language I'm
         | working on which internalizes that mechanism at the language
         | level :)]
         | 
         | https://www.brandons.me/blog/the-bagel-language
        
       | memco wrote:
       | In the final example there is a create effect and an unsafe
       | create effect: could these be collapsed such that something
       | checks for any argument that makes the call unsafe and defer to
       | the unsafe version but otherwise chooses the safe version? Seems
       | the split still relies on the human to know which one they need
       | to use without that extra bit of logic.
        
       | draw_down wrote:
        
       | kubb wrote:
       | considered by you. for me they're the best way to write react
       | components, and i was never confused as to how they work.
       | 
       | also the post is laser focused on effect hooks which are arguably
       | the most difficult to deal with because of their intended
       | application (reflecting state updates outside of the virtual DOM)
        
       | simonlc wrote:
       | News: People don't learn the tools so mistakes are made Author:
       | Because I see mistakes, these tools are bad
       | 
       | I consider your teammates harmful. All this stuff is mentioned in
       | the excellent React documentation.
        
       | serguzest wrote:
       | In our codebase, we use useEffect without exhaustive-deps linting
       | rule. I don't think it is bad in hands of developers who have
       | basic understanding of closures in javascript. Otherwise, we have
       | to resort to complicated logic to provide same functionality like
       | keep tracking of changed variables. Are we wrong to do that?
        
       | lxe wrote:
       | > This decision forces the programmer to be responsible for
       | making explicit those implicit dependencies, thereby functioning
       | as a "human compiler" of some sort. Declaring dependencies is
       | manual boilerplate work and error-prone, like C memory
       | management.
       | 
       | I think hooks solved some problems at the cost of introducing
       | others, such as this. It's unclear to me still whether the value
       | trade-off has been worth it.
        
       | bitwize wrote:
       | No, "state" doesn't mean "keeping things around while I compute
       | other things". That's just intermediate results. State is defined
       | by its ability to change as the computation runs; and the changes
       | can affect how the computation unfolds. That's why it's "evil":
       | uncontrolled state changes are difficult to reason about and are
       | great cover for bugs.
        
       | didip wrote:
       | This is just a casual observation from a back end/infrastructure
       | guy perspective.
       | 
       | Does building a rich GUI experience on a web application need to
       | be this complicated?
       | 
       | I still remember the days when rendering are mostly done on
       | server side and Javascript was used as progressive enhancements.
       | The web application back then were quite interactive and building
       | them were somewhat simpler than the current state of the art.
        
         | pandesal wrote:
         | You don't need something like React for building rich GUI today
         | but once you have a sufficiently large codebase and a large
         | team of developers, you're going to end up building inferior
         | in-house versions of libraries/frameworks like React/Redux just
         | to have any sane structure in maintaining the application as
         | well as support future feature development. The currents state
         | of front-end is complicated but I absolutely prefer it over the
         | chaos of old timey vanilla javascript.
        
         | steve76 wrote:
        
         | AgentME wrote:
         | React is a great way to do progressive enhancement: you can
         | have the same component code run on the server and client. On
         | the server, it will only be used for creating static html. On
         | the client, the component code will bind to the already
         | generated HTML and allow client-side interactivity.
         | 
         | Writing rich UIs with progressive enhancement without this
         | benefit was over-complicated pain before React. All of your UI
         | code would either be based around binding to static HTML
         | generated by the server or be based on locally-generated
         | elements; if you ever had a widget that had been generated by
         | the server that you now want to generate client-side in some
         | context, or vice-versa, then you had to have multiple separate
         | code paths on the client for that in addition to the separate
         | server-side code for the widget. Having the component's code
         | defined once in a way that works for all three contexts
         | (client-side generation, server-side generation, client-side
         | binding to server-generated HTML) is great.
         | 
         | I think people assume that because React is a newer way of
         | doing things that it doesn't work well at the old goals
         | (progressive enhancement) but the opposite is true!
        
       | raiflip wrote:
       | FP has also benefited from a hype cycle, and FP does improve code
       | in a lot of ways. Making your data immutable makes it easier to
       | reason about, and pure functions prevent surprises. However if
       | your argument is state is bad, and FP avoids state, so that is
       | why it is good, but you encounter a scenario in which state is
       | required, then the benefits of FP start to degrade.
       | 
       | The reality is closer that, state should be immutable, and
       | minimized as much as possible, but at the end of the day, almost
       | every interesting problem requires storing state. Once you reach
       | that point, classes are simply a better solution for state than
       | closures. Especially if your class and its variables are
       | immutable, you get all the benefits I mentioned and none of these
       | tradeoffs. Your state is explicitly stated.
        
       | hughw wrote:
       | Using ImmutableJS [1] for all the state variables avoids the
       | identity issues he raises.
       | 
       | [1] https://immutable-js.com
        
       | i_like_robots wrote:
       | I was a very early adopter of React and I am very grateful for
       | it. It's now it's my job to listen carefully to the concerns of
       | my teams and I've helped developers of all abilities and
       | experiences through their troubles on all sorts projects over the
       | years. In that time I'm sure I must have discussed all the good
       | and bad things that can be said about React but if I'm honest I'm
       | not hearing as much good nowadays.
       | 
       | I think the part of the reason React has been so successful is
       | because its rules are easily communicated; components which
       | render HTML or composed other components, state lives inside
       | those components and props are passed down between them. How to
       | deconstruct an interface into individual pieces and how data
       | should flow through them really resonated with a lot of people.
       | 
       | But I think the shift to hooks means we've have lost the clear
       | rules that made React so accessible to newcomers. Although hooks
       | are still easy to get started with they seem to create confusion
       | easily, and one wrong dependency or deriving state incorrectly
       | and your laptop becomes a heater. This makes it more difficult
       | for developers to focus on what they're meant to be building
       | because their heads are filled with an uncertain palette of
       | distributed logic.
       | 
       | Now that the tiny API and rapid learning curve seem to have been
       | abandoned I'm starting to think React may no longer designed to
       | help solve the problems me and my teams are being asked to solve
       | and perhaps the reason why hooks aren't clicking for many people
       | isn't because they aren't smart or willing enough, it's because
       | the mental model required to use React effectively no longer
       | overlaps enough with the things we're usually building.
        
       | gitgud wrote:
       | The proposed solution seems to gloss over many of the trade-off's
       | that you're making.
       | 
       | > _Most bugs can be solved by moving hooks away from the
       | components_
       | 
       | Well you can't modify state from outside the component, and the
       | context becomes harder less clear... to me hooks are still the
       | best option.
        
       | daok wrote:
       | I've been using SolidJS for some personal project and they use
       | most of React concept but avoid pitfalls by running once the code
       | of each component. Also, no need to explicit dependencies! The
       | framework figures it out automatically. Really nice dev
       | experience.
        
         | dham wrote:
         | It's weird after using Mobx since 2017, that a framework seems
         | revolutionary because it can figure out dependencies. React has
         | really brainwashed us.
        
       | jakelazaroff wrote:
       | useEffect is a foot gun, agreed. But in the large React codebases
       | I've worked with, my team has had basically zero issues with any
       | of the other hooks.
        
       | steinuil wrote:
       | The main problem is that the hooks provided by React by default
       | are low-level primitives of an incremental computing DSL, and you
       | need to have a very good understanding of both JavaScript and the
       | semantics of this DSL to work with those primitives correctly; it
       | feels like having to reimplement strcat every time-- with every
       | pitfall that you might fall into while reimplementing strcat in
       | C.
       | 
       | Maybe this could be solved by a hooks "standard library" that
       | provides generally useful hooks like useTimeout and a useMemo
       | that is actually stable as mentioned in @purplerabbit's comment.
        
       | [deleted]
        
       | akagusu wrote:
       | I don't know why, but all this story made me think about C memory
       | allocation and undefined behavior.
       | 
       | Perhaps should I ditch hooks for Rust?
        
       | serverlessmania wrote:
       | What classes vs function have to do with OOP vs Functional??
        
       | drc500free wrote:
       | 17 years ago, a colleague convinced the team to rewrite a Java
       | server application using Aspect Oriented Programming. It was a
       | new and magical technology that would allow us to separate the
       | meaningful business logic from annoying logging and bookkeeping -
       | at the slight cost of losing track of standard OO control flow
       | and scoping.
       | 
       | The 12 months of hell that followed cannot be described in a
       | simple hn comment. The dangers of any technology that confuses
       | code reviewers about scope is one of a small handful of lessons
       | that have stuck in my brain, almost a decade after I left
       | engineering behind.
        
       | leroman wrote:
       | Maybe the comperator should be pluggable so we could opt for a
       | deep equals, or what I'm thinking along the lines of- (for better
       | performance)
       | 
       | Object.is(..) || deepEquals (...)
        
       | thrwy_ywrht wrote:
       | Bugs caused by faulty equivalency-related code have been common
       | for a long, long time in React development. I couldn't guess how
       | many bugs related to object destructuring I wrote, and fixed,
       | long before hooks existed (although I can confidently say the
       | former number will be significantly higher than the latter). I
       | used to see these _all the time_ on large Redux /React codebases.
       | `mapStateToProps` was always a fun place to find them.
        
       | skydhash wrote:
       | I'd said that it is a non-issue. One of the main tenets in React
       | is that your component re-renders when the prop changes. I think
       | anyone should research what exactly change in this context means
       | and take care of not triggering the re-render when it's not
       | wanted. The author goes out of the way to use examples that are
       | what I would consider bad code and not something that should ever
       | pass a code review.
       | 
       | I have not dealt with many junior devs, but creating an object
       | either by using a literal or restructuring is still creating a
       | new object and no one should expect it to be the same. Equal
       | perhaps, but not the same. Maybe someone should explain scope and
       | instance lifetime in the JavaScript world instead of blaming
       | React for these. Because there is no surprise that the component
       | re-render when you change the prop.
        
         | masylum wrote:
         | The point is that "a prop changed" can't be reasoned within a
         | component. If I receive an object as a prop, I can only reason
         | if that pointer in memory will be the same, not wether it has
         | changed. The allocation can happen up in the component
         | hierarchy making this a subtle for even the most senior
         | developers.
        
           | paulmd wrote:
           | in java terms, it sounds like you need to define your own
           | "equals()/hashCode()". The "reference equality vs logical
           | equality" is well-understood in that domain at least.
        
           | skydhash wrote:
           | Props are outside the boundary of concerns for a component as
           | far as allocation go. You react to value and identity. The
           | parent component is the one concerned with actually giving
           | you the correct props. Very much like a function call would
           | work. You can provide a signature or a contract, but you are
           | not actually expected to deal with every kind of abuses,
           | especially things that fall outside good practices.
           | 
           | Perhaps it is a concern in bigger codebase. AFAIK, there is
           | no method to enforce this kind of contract. But documentation
           | could be a big help. Like documenting how changes to a prop
           | impact the behavior of the component - like the common
           | `initialValue` and `value`.
        
           | andrewingram wrote:
           | That's why a number of people (myself included) advocate for
           | overzealous/defensive use of useMemo/useCallback as a means
           | of ensuring that a prop changing is always meaningful. The
           | rationale is summarised here:
           | https://www.zhenghao.io/posts/memo-or-not
           | 
           | There are good reasons for _not_ doing this, since using
           | these hooks isn't free; and technically speaking useMemo
           | isn't an identity guarantee (though it currently behaves as
           | one), but I haven't experienced any of the common useEffect
           | pitfalls since adopting this methodology a couple of years
           | ago.
           | 
           | But as my sibling comment points out, a lot of the need for
           | this defensive coding would go away if there were more ways
           | of defining equivalence. I hope that one day the record and
           | tuple proposals land, which should help a bit. But i'd also
           | like to see something like Python's __eq__ and __hash__ in
           | JavaScript too - perhaps done in a similar way to
           | [Symbol.iterator].
        
         | clansimus wrote:
         | Yea, the examples seem disingenuous to me.
         | 
         | > Most bugs can be solved by moving hooks away from the
         | components and using primitives as the only dependencies.
         | 
         | Or just use primitives in the dependency array of your existing
         | hooks?
        
           | randall wrote:
           | Exactly.
        
         | bstar77 wrote:
         | This was my overall thought as well. When I get myself into the
         | type of trouble he's describing, it's usually because I either
         | designed the effect poorly or it's simply doing too much. I
         | also find that I'm abstracting certain complex effects into
         | Redux Sagas more and more which solves some issues around
         | effects depending on the result of other effects- not all
         | processes should be triggered in this way.
         | 
         | It took me a long time of grinding on various effects scenarios
         | to figure out efficient, easy to understand solutions to
         | complex behaviors. That said, I do agree with his points on
         | under/over subscription... that is still something that
         | frustrates me, especially when the linter wants me to
         | complicate something that seems unnecessary.
        
       | jchook wrote:
       | Kind of neat how SolidJS solved these issues: each component
       | function and hook runs only once.
       | 
       | There is no need to worry about dependencies since it won't need
       | to run again! Hooks like useState() do not emit a value -- they
       | emit a "signal", a function that returns the current value.
       | 
       | This article introduced me to the concept:
       | https://typeofnan.dev/solid-js-feels-like-what-i-always-want...
       | 
       | Unfortunately there's no react-native equivalent and the
       | ecosystem is much smaller, but I have to imagine the React team
       | has their eye on this alternative strategy.
        
       | andrewstuart wrote:
       | OK so criticisms heard.
       | 
       | What is a solution?
        
         | chrisco255 wrote:
         | Read docs, implement linters. It doesn't take much time to
         | learn the dependencies and nuances of useEffect (a single
         | afternoon of reading the docs).
         | 
         | Otherwise, you can always still use Redux/Rematch or class
         | components (they're still there) or any other state management
         | solution and just pass in props.
        
       | martimarkov wrote:
       | I know this might be a bit off topic but I wanted to mention it:
       | FactorialHR had an forever free plan[1] which they killed and
       | forced me to go onto a paid plan. This happened after I
       | recommended them to a lot of founders which then backfired on me
       | (albeit a bit - ppl still trust my opinion). Needless to say all
       | those startups migrated to a different HR provider cuz they just
       | couldn't trust a company like that with sensitive data.
       | 
       | But it's just the way they did it and how after that there was a
       | complete ghosting on my requests and queries.
       | 
       | 1 -
       | https://web.archive.org/web/20190425210338/https://factorial...
        
       | PaulHoule wrote:
       | Nice to see hooks get some tough love.
       | 
       | Too many people think 'functional programming' is "not OO" but
       | there is also that bit about "no side effects" and hooks are all
       | about side effects.
        
         | nightski wrote:
         | The entire "no side effect" aspect of functional programming is
         | just a huge misunderstanding at best. It's unfortunate so many
         | pushed that narrative. Many FP languages do not even restrict
         | side effects. But those who do, like Haskell, do so in order to
         | communicate where those side effects are taking place.
         | 
         | In Haskell for example you can put all of your code in the IO
         | monad and just have side effects anywhere. This works fine. But
         | you quickly realize that there are benefits to separating out
         | code with side effects from code without. The types make this
         | clear. Haskell provides powerful mechanisms to weave functions
         | that both have side effects and those that don't with ease
         | while maintaining that clear separation.
         | 
         | If anything FP in this manner is an extremely powerful version
         | of side effects. It's not about "no side effects at all" but
         | rather taking control of them and using them to our advantage.
        
           | horsawlarway wrote:
           | > But you quickly realize that there are benefits to
           | separating out code with side effects from code without.
           | 
           | This belies your whole previous argument...
           | 
           | Everyone understands that side effects are a requirement
           | (literally - a program with no side effects is useless).
           | Functional programming herds the programmer into a situation
           | where code that creates side effects is consolidated into
           | just a few places, and the _majority_ of the code is pure
           | functions.
           | 
           | That paradigm has a real cost - consolidating side-effects
           | isn't particularly easy, and you have to work to do it.
           | 
           | But in exchange you get a LOT of pure functions that are
           | 
           | 1. Easy to reason about
           | 
           | 2. Easy to compose (because they have no side effects)
           | 
           | 3. Easy to test
           | 
           | Hooks are the antithesis of this - they create code them
           | _seems_ pure, and has the guile of being composable  &
           | testable, but in reality they are very hard to reason about.
           | They have completely undone the work of consolidating state
           | and side-effects into one location. It is very easy to call a
           | function with a hook in it in a way that breaks that
           | function, and it's usually hard to reason about what subtle
           | differences are causing this new breakage.
        
             | bern4444 wrote:
             | > Hooks are the antithesis of this - they create code them
             | seems pure
             | 
             | I disagree. The presence of a hook is the indicator that
             | something impure is happening. Seeing a hook should be
             | equivalent to seeing a promise, option, IO type etc.
             | 
             | Hooks also compose beautifully together. You can make so
             | many great new hooks by combining just useState and
             | useEffect together, bundling up that functionality into a
             | new hook that you can then use in any UI.
        
               | horsawlarway wrote:
               | > The presence of a hook is the indicator that something
               | impure is happening.
               | 
               | Yes. And that's my whole point.
               | 
               | React was very powerful when care was taken to place
               | impure code into a single class based component, that
               | then passes state down to pure components as props.
               | 
               | React is a lot less powerful when developers scatter
               | hooks _everywhere_.
               | 
               | New developers no longer have to go out of their way to
               | understand the render lifecycles of a class based
               | components, and feel the pain of writing
               | componentDidMount or componentWillMount or
               | componentWillUnmount or shouldComponentUpdate functions.
               | Instead they just throw a hook in. Which is mostly ok -
               | but it's hiding that you do actually still have to care
               | about how this whole shindig works (and opens up a whole
               | new world of pain around identity and equality checking,
               | re-render cycles, dependency passing, etc)
               | 
               | I'm not saying hooks don't have an upside (ex: I'm right
               | there with you, I mostly prefer a hook to an HoC from a
               | reusability stand point) but hooks let developers shove
               | their head into the sand and mostly pretend that they're
               | writing a pure function - and they're ABSOLUTELY NOT.
        
               | nightski wrote:
               | There was nothing preventing you from scattering state
               | everywhere in class based components. On top of this the
               | component tree became a huge mess of HoC's stacked on top
               | of each other.
               | 
               | You absolutely should not be scattering hooks everywhere
               | in your code base. The same principle applies to use them
               | higher in the hierarchy and pass down props.
               | 
               | This is a simple principle that can be taught to a new
               | React developer. Keep your state at the highest level it
               | makes sense to no matter the state mechanism used.
               | 
               | Hooks allow for composition of effects in a way that
               | class based components did not.
        
               | horsawlarway wrote:
               | > There was nothing preventing you from scattering state
               | everywhere in class based components.
               | 
               | There was though - it's the same pain you're referring to
               | later... "Hooks allow for composition of effects in a way
               | that class based components did not."
               | 
               | Class based components sucked in a _lot_ of ways. But the
               | nice side effect of that was that folks tended to use
               | them more carefully, and avoid using them when they didn
               | 't understand them (or at least avoid implementing any
               | method besides render()).
               | 
               | I'm not saying hooks don't have nice properties - I'm
               | saying that I'm not convinced (after using hooks for
               | about 2 years now) that the price you pay is worth it.
               | 
               | The number one source of bugs in our codebase is...
               | drumroll... hooks. I think a part of that is that state
               | in general is evil, and will be where most of the bugs
               | lurk. But I think the other side is that hooks have a
               | completely new, unintuitive, hard to reason about set of
               | rules. Composable? Sure, sometimes, if you work really
               | hard to understand exactly what sort of new rules you're
               | creating and then hiding in their complexity. Intuitive?
               | Fuck no!
        
               | nightski wrote:
               | We can just agree to disagree then. I'd find libraries
               | all the time on github which had class components using
               | state in weird ways you wouldn't expect.
               | 
               | It sounds like your org could use some simple guiding
               | principles and code reviews. You seem experienced, this
               | shouldn't be a big problem. Maybe help guide your junior
               | devs?
        
               | horsawlarway wrote:
               | Eh - I'm not really sure we're even disagreeing. I just
               | think that hooks let you stack the abstraction tower a
               | lot higher, and answering some fairly basic questions can
               | become really hard.
               | 
               | There's power there, and I absolutely agree that hooks do
               | a better job of making for re-usable code than HoCs, I
               | just think that the general level of understanding for
               | them is low, and most devs do a really poor job reasoning
               | about them (and in generally - I find they're basically
               | impossible to reason about in isolation).
               | 
               | I see people do things like wrap everything in useMemo
               | and useCallback, or pass complex objects to useEffect as
               | deps, or fail to understand that making the output of
               | useState the dependency of a useEffect hook that happens
               | to call the corresponding setState function is a recipe
               | for lockups, or any number of other fairly simple
               | mistakes.
               | 
               | Plus... tools like redux strongly encourage destructuring
               | semantics, and destructuring for hooks is absolutely the
               | wrong thing (for the same reason - equality and identity
               | checks). But then you're in a conversation about object
               | identity and memory locations with a dev who has never
               | encountered a pointer in their life, who's 6 months out
               | of a bootcamp, and whose eyes are glazing over further
               | and further with every word out of your mouth.
               | 
               | Worse - hooks can give you a loaded gun if you expect all
               | the environments your code runs in to act like a browser
               | (see my useLocation example with JSDom). Works a-ok when
               | tested in a browser. Will even work nicely for the
               | specific tests you might write for your component (since
               | folks generally mock their hooks) but will absolutely
               | foot-gun you if another spec calls the real hook. Happens
               | to eat up a boatload of CI cpu usage and time as well.
        
             | PaulHoule wrote:
             | Some things are easy to express as functions (compilers).
             | Other things aren't (user interfaces).
             | 
             | Even when immutable data is easy and is good from a
             | software design perspective it is often a terrible choice
             | from a performance perspective. Advocates say the
             | performance loss is just a factor of two in many cases but
             | that's why FORTRAN survived so long against C, why people
             | are developing Rust when Java is available, etc.
        
               | horsawlarway wrote:
               | I don't disagree with you at all.
               | 
               | There's a reason no on is writing modern games in
               | functional languages, and that reason is performance.
               | 
               | But that said - At least for me - the major attraction of
               | React was that it really concentrated on making ui
               | related code pure. Give a component the same props, and
               | you get the same DOM.
               | 
               | That's a really powerful concept for reducing bugs,
               | easing testing, and giving you composable components.
               | 
               | It is not a performance improvement.
               | 
               | I think hooks really hollowed out the value proposition
               | here. Because class based components were more painful, I
               | used to see a lot of care and thought put into
               | consolidating the logic that generated props into a
               | single class based component (consolidating state). That
               | component would then mostly pass down props to pure
               | components.
               | 
               | Hooks make it easy to just throw state into any old
               | component - which is nice in some sense, but like I said
               | - it hollows out the value proposition of having pure
               | components.
               | 
               | Good teams will still try to write mostly pure
               | components, but many folks will just liberally scatter
               | hooks into their code, creating code that becomes
               | increasingly hard to reason about.
        
         | horsawlarway wrote:
         | I agree.
         | 
         | I've had several conversations where fans of Hooks will justify
         | them by saying that "functional programming is about
         | composition over inheritance".
         | 
         | And I think that's entirely missing the point of functional
         | programming. The goal wasn't to remove inheritance in favor of
         | composition, it was to remove _STATE_ - which in turn results
         | in the nice property that functions can be composed, because
         | they take all relevant data as arguments (they are pure).
         | 
         | Hooks basically blow that away - you've added back in all the
         | problems of local state, but now you've hidden it behind a
         | brand new paradigm that developers just don't have a very good
         | feel for (even years after the introduction of hooks).
         | 
         | I'm reasonably well-versed with hooks, and even I find myself
         | having to do incredibly complicated and deep dives into
         | upstream code to answer simple questions, like "How many times
         | will this hook run?" or "How many render cycles will this hook
         | introduce?".
         | 
         | Sometimes the answer is so far upstream it's basically
         | impossible to answer without running code - Ex: if you depend
         | on the "useLocation" hook from react-router-dom, and you pass
         | the entire object as a dep to useEffect (which is a mistake in
         | and of itself), you will be fine in the browser, but Jest tests
         | will trigger an infinite render cycle, because JSDom generates
         | a new object for each call of window.location.
         | 
         | I can reason about functions that are pure, and that's the
         | freaking point of functional programming. I cannot reason about
         | functions with hooks in them - it's FAR worse than class based
         | components in basically every way _except_ ease of re-use.
         | 
         | I think in many respects - we threw out the baby with the
         | bathwater.
        
           | whoisthemachine wrote:
           | > And I think that's entirely missing the point of functional
           | programming. The goal wasn't to remove inheritance in favor
           | of composition, it was to remove STATE - which in turn
           | results in the nice property that functions can be composed,
           | because they take all relevant data as arguments (they are
           | pure).
           | 
           | I came here to see this said...regardless of the method used,
           | _state_ is what is challenging to maintain, regardless of how
           | your framework or tool modifies and tracks it. And the only
           | way I know of to properly wrap some sense of sanity around
           | complex state modification is with unit tests, again,
           | regardless of framework /tool. If you can't test it with a
           | unit test, then you're going to struggle manually testing it
           | as well, even if it does usually work.
           | 
           | A side-note is that I always thought the obvious split for
           | functional/class-based React components was
           | stateless/stateful (as full-blown objects are basically
           | purpose-built for tracking state), so I was surprised when I
           | joined this new project at my employer and learned about the
           | interesting world of hooks. I rarely dabble in React however.
           | 
           | My snarky side today wants to add "developers struggle with
           | maintaining state, what else is new".
        
         | [deleted]
        
       | ajkjk wrote:
       | No way, hooks are great. All of these problems exist in Class
       | components too -- especially accidentally rebuilding `params` in
       | `mapStateToProps` or whatever. Figuring out how to do things on
       | re-renders with componentDidMount and componentDidUpdate is a
       | total disaster. Don't even get me started on
       | getDerivedStateFromProps. God, hooks are better in every way.
        
       | ezekg wrote:
       | I'm not a fan of hooks, really -- it feels like a big black box.
       | I tried to find the source code, but I wasn't able to grok how it
       | all actually worked. Maybe just me, but hooks are a bit too
       | magic-ey for my tastes.
        
         | i_like_robots wrote:
         | This article helped me a lot, it's still a large amount of
         | information to internalise though: https://medium.com/the-
         | guild/under-the-hood-of-reacts-hooks-...
        
         | brimble wrote:
         | I read it when they first announced it. There was _a lot_ of
         | indirection, which is probably why you had trouble, but in the
         | end, hooks were registered to a list that was associated to
         | your component object (yes, object) in the big, central state
         | machine that is React.
         | 
         | It may have changed since then, but hooks were basically just
         | weird shitty methods & properties with silly non-standard
         | declaration syntax and totally bizarre access/invocation rules,
         | in a language that already had fairly normal objects & methods
         | and all that.
        
           | postalrat wrote:
           | Because reusing code in those class based components was
           | never as easy as creating a new hook.
        
         | barbecue_sauce wrote:
         | I haven't really used React much since hooks were introduced
         | (not because of hooks, just a coincidence). Up until that
         | point, I had been using React almost daily. At the time, I
         | remember thinking "What is this for? What problem does this
         | solve in my already well-architected and organized front-end?"
        
         | dkarl wrote:
         | For this reason I was skeptical of them from the start. It's a
         | broken formula: X is challenging for some developers to
         | understand, so we'll replace it with Y which practically nobody
         | will understand, but it's okay because they won't need to.
         | 
         | In order to work, the formula has to follow Dijkstra's
         | admonition about abstraction: "The purpose of abstraction is
         | not to be vague, but to create a new semantic level in which
         | one can be absolutely precise." The implementation of Y can be
         | opaque to users, but it has to be precisely defined on the
         | level that users think about it.
         | 
         | Hooks were not presented this way. There was never a precise
         | definition of hooks presented to users, at least not one that I
         | could find in a succinct form in the documentation. To me, the
         | documentation amounted to a handful of examples and a couple of
         | rules to follow, and you were supposed to pattern-match your
         | way to success, without any precise definition to fall back on
         | when you were uncertain.
        
       | purplerabbit wrote:
       | You can get around this identity problem by creating all
       | derived/composed objects via `useMemo`. This ensures that their
       | identity only changes when that of their dependencies do. This
       | lets you get around this "identity problem", but comes with some
       | issues:
       | 
       | - Relying on `useMemo` preserved object identity assumes a
       | semantic guarantee, which React docs tell us explicitly not to do
       | [1]. Not providing this guarantee is ridiculous. If their cache
       | is implemented correctly, this should be no problem.
       | 
       | - The alternative is to leverage an external lib, which does
       | provide this guarantee [2]. However, it's weird that bringing in
       | an external lib as the more "correct" solution to this incredibly
       | common problem (this is seriously relevant to like 1/2 the
       | components I write)
       | 
       | - Wrapping every bit of derived state in a `useMemo` hook is
       | incredibly verbose and annoying, especially when you take
       | dependency arrays into account. I feel like I'm writing generated
       | code.
       | 
       | 1. https://reactjs.org/docs/hooks-reference.html#usememo
       | 
       | 2. https://github.com/alexreardon/use-memo-one
        
         | codeflo wrote:
         | Two thoughts.
         | 
         | One, you don't rely on a semantic guarantee if you use useMemo
         | for derived state. Avoiding rerendering counts as an
         | optimization as far as the React docs are concerned (your
         | program works if there's an extra render), and this is in fact
         | exactly what it was intended for. The docs you linked seem to
         | agree: Regardless of whether an offscreen component keeps or
         | doesn't keep its useMemo, the code is correct and there's at
         | most one extra render.
         | 
         | Second, while I agree with the verbosity complaint, I
         | personally make a point to use useMemo as coarsely as possible.
         | It's often completely fine to compute all derived state in a
         | single big lambda that returns one big (immediately
         | destructured) object. It's only when you have multiple pieces
         | of derived state that update individually and are also all
         | expensive to compute that you actually need fine-grained
         | useMemo calls. And in this case, you can always think about
         | extracting sone of that logic into a helper function/hook.
         | 
         | It's not perfect, but I think it's possible to avoid a lot of
         | the pain most of the time.
        
           | purplerabbit wrote:
           | I'm with you on thought #2. Regarding your first thought,
           | however: if you want control over when `useEffect` callbacks
           | fire, identity isn't just an optimization, it's a necessity.
           | For example (used in another comment): if you're not using a
           | smart intermediate layer like `react-query`, you can
           | unintentionally trigger loading states and re-fetches if
           | you're not closely watching dependency array identities
        
             | codeflo wrote:
             | Oh, that's a good example. I'll argue that you should try
             | to use primitives (strings/numbers) as keys in those cases.
             | But if you can't, then you're right that identity is
             | critically important.
        
             | acidbaseextract wrote:
             | I'm curious if you've used useDeepCompareEffect, the use-
             | deep-compare-effect npm package? I've found that it is
             | pretty reasonable foolproofing for many of these identity
             | questions. I'm well aware of Dan Abramov's objections to
             | the deep equality checking [1] but I still find it a bit
             | easier for me and other devs to reason about when doing
             | things like data fetching.
             | 
             | [1]
             | https://twitter.com/dan_abramov/status/1104414469629898754
        
               | purplerabbit wrote:
               | Hadn't heard of that -- thanks for sharing!
               | 
               | Crazy how many choices there are in the mix (use-deep-
               | compare-effect, the `JSON.stringify` approach mentioned
               | by Dan, `useMemo`, and `useMemoOne`). Feels like a "pick
               | your poison" scenario, as each one has a significant
               | issue.
               | 
               | That being said, `useDeepCompareEffect` does seem the
               | most "foolproof", and "foolproof" is probably more
               | important than intuitive or performant in most cases.
        
         | srcreigh wrote:
         | Why do you need identity except for increasing performance?
        
           | purplerabbit wrote:
           | Identity is necessary if you want to predictably trigger
           | `useEffect` callbacks.
           | 
           | Example: if you're not using a smart intermediate layer like
           | `react-query`, you can unintentionally trigger loading states
           | and re-fetches if you're not closely watching dependency
           | array identities
        
             | WorldMaker wrote:
             | Though this is also a strong reminder that though useEffect
             | can entirely replace service layers and state management
             | layers such as react-query/Redux/Mobx/Relay/what-have-you
             | doesn't mean it necessarily _should_. (Ultimately that 's
             | the bottom line summary from this article.) useEffect is a
             | very "raw" low-level tool, at some point it is a good idea
             | to consider a higher-level tool (maybe one based on
             | useEffect under the hood).
             | 
             | Don't forget too that trying to do everything in raw
             | useEffect code may be a sign of putting too much business
             | logic in your views and abstracting that out can be a good
             | separation of concerns no matter how you decide to abstract
             | that service layer (and/or which tools like react-
             | query/Redux/Mobx/etc you choose to make that easier).
        
       | stevebmark wrote:
       | Maybe this author doesn't understand the history of the term
       | "considered harmful"? The points are valid, and there's lots else
       | to be wary of in hooks, but the title shows a lack of experience.
        
         | djbusby wrote:
         | Nah, "considered harmful" is like the OG click-bait; intended
         | (IMO) to spark discussion.
         | 
         | https://en.m.wikipedia.org/wiki/Considered_harmful
        
           | masylum wrote:
           | yes, it was a wink/homage to historic discussions on anti-
           | patterns :)
        
       | Igrom wrote:
       | I respect the opinion that JavaScript can be an unintuitive
       | language to write in. The concurrent existence of abstract and
       | strict equality operators, the former's lack of predictability,
       | as well as their performing shallow comparison are all a fount of
       | problems.
       | 
       | While I can't vouch for the relevancy of the book nowadays,
       | reading "JavaScript: The Good Parts" when I was starting out
       | myself a) conferred a decent knowledge of the gotchas and b)
       | helped me understand that paying the cost in decreased concision
       | to fence off the dangerous parts of the language (e.g., the
       | abstract "==" comparison operator) was very much worth it.
       | Nowadays, with the abundance of linters and the existence of
       | TypeScript, hopefully the better part of JavaScript programmers
       | get to code in a safer, stricter subset of the language --
       | nevertheless, as the author points out, one can trip anywhere.
       | 
       | That being said, how do any of the above deficiencies constitute
       | an unique indictment of React Hooks, and not, say, that of other
       | UI frameworks or the language in general? React Hooks have
       | introduced neither closures nor shallow comparison to the
       | language. Most of the author's grievances are addressed fairly
       | clearly in their comprehensive official documentation[0] (people
       | still read manuals, right?) or quickly become self-evident
       | through practical usage. Owing to the framework's popularity,
       | linter support for hooks is also extensive, with one's code
       | already being automatically verified against the majority of the
       | documentation's commandments. There's probably not much more than
       | a handful of classes of errors that a developer has to manually
       | watch out for.
       | 
       | I don't mean to say that there's nothing wrong with hooks, but a
       | comparative review that pits them against other frameworks would
       | have been more constructive.
       | 
       | Lastly, the use of the "considered harmful" moniker in the title
       | in spite of the relative scantness of constructive criticism in
       | the article lies somewhere between clickbait, scaremongering and
       | false expertise. It's to be considered harmful[1].
       | 
       | >Hooks benefit from closures because they can "see" and retain
       | information from their scope, for instance, in the example above,
       | user. However, with closure dependencies being implicit, it is
       | impossible to know when to run the side-effect.
       | 
       | The frequency at which an effect is to run is wholly orthogonal
       | to whether the associated function accesses any variables in its
       | environment; it is decided by the developer through setting the
       | dependency array, which is passed as a separate argument to the
       | useEffect function. No relation whatsoever.
       | 
       | [0] e.g., https://reactjs.org/docs/hooks-
       | reference.html#usecallback [1]
       | https://news.ycombinator.com/item?id=9744916
        
       | quxpar wrote:
       | I want people to be critical of the method du jour, but these
       | 'production-like code examples' defy most of the modern
       | conventions that a React dev would use. For example the fetch
       | example seems to ignore its own prior destructuring assignment to
       | make an awkward reference to an object property.
        
         | masylum wrote:
         | I agree, but the problem is that when you refer an object
         | inside a hook, the eslint exhaustive deps will tell you to add
         | that object as a dependency. In that example, it's easily
         | solved with destructuring `const teamId = params`, but a lot of
         | developers will blindly follow the eslint complaint.
        
           | clansimus wrote:
           | In that example, eslint will tell you to add `params.teamId`
           | as a dependency, not `params`
        
       | pictur wrote:
       | The main problem with hooks is that it takes a lot of somersaults
       | to do simple things. Just like class components, there are things
       | at the core that are not kept simple. which makes hooks very
       | complex.
        
       | Tade0 wrote:
       | > It was such an innovative API that it took the frontend world
       | by storm.
       | 
       | Only the React community actually. It's the majority, but I
       | wouldn't equate it with the _whole_ frontend community.
       | 
       | I remember when they were introduced - it was during the brief
       | moment(1 year) during which I worked in React.
       | 
       | My code-smell-o-meter indicated that this is going to cause
       | problems in the long run.
       | 
       | Ultimately it did, since you can't just replace everything with
       | hooks and call it a day, as that is likely to cause _massive_
       | performance issues.
        
       | kietay wrote:
       | > It is then safe to say that the only difference between
       | programming paradigms is how long you keep stuff around and the
       | space-time tradeoffs that these decisions entail.
       | 
       | That is absolutely not the only difference.
        
       | koprulusector wrote:
       | _facepalm_
       | 
       | > The mechanisms to retain memory have a lot in common. Classes
       | use this, which refers to the object's instance, while functions
       | implement closures - the ability to remember all the variables
       | within their scope.
       | 
       | Non-arrow functions, like the example, defined with the
       | `function` keyword also have `this` context. If you want to limit
       | `this` and rely solely on closures, you probably want an arrow
       | function.
        
       ___________________________________________________________________
       (page generated 2022-03-21 23:01 UTC)