Live data from Hacker News

Why Do React Hooks Rely on Call Order?

overreacted.io

101–110 of 115 posts

Re: Why Do React Hooks Rely on Call Order?

#101
post #79

Earlier quoted context omitted.

Okay, DX is an important selling factor, so your approach seems reasonable to me.

Runtime performance is another one. Map lookups aren’t free, especially if there’s a whole bunch of them happening in every component on every render.

Yes, the order restriction buys a lot of things rather cheaply.

And while I trust the React teams judgement, I teach people React and they often question the "why".

Re: Why Do React Hooks Rely on Call Order?

#102
My gut feeling tells me that introducing hooks is going to end badly.

If there's no patently obvious advantage but you have to rely either on convention or an additional pool of knowledge then most junior (or generally less skilled) developers cannot be trusted to use the given thing properly.

I've seen this happen with observables - sure you can do a lot of new stuff with them but they are only clearly more useful than say promises in a handful of cases.

Thid trend of producing tools which are powerful in the hands of the best but hard to use for beginners worries me. In the long run this makes development more expensive, not less.

Re: Why Do React Hooks Rely on Call Order?

#103

Earlier quoted context omitted.

Maybe something like this? https://gist.github.com/sebastiaanvisser/72e6bc54baa14abc08e... It all seems to boil down to packing and silently composing lifecycle methods. Either with classes, records of functions, or effectful functions, dictionaries. I can see how the ergonomics of current react hooks are actually great, but I still think they’re weird :) I’ll probably get over it some day. edit: note how hooks are n…

How would you pass values between two different Hooks? See my two examples in the post (useSpring and useFriendStatus). Note they need to be always up-to-date and not just execute once.

With passing values between hooks do you mean "Make one hook depend on the value of another hook?". Because that seems like the functor 'map', or applicative 'apply'.

Note, I don't really know what these springs are, but derived hooks could work like this:

    class MyComp extends ReactishComponent {
      fast = this.use(new Spring({ pos: [0, 0], config: "fast" })) // prim hook
      slow = this.use(this.fast.map(spring => ({ pos: spring.pos, config: "slow" }))) // functor map
      mid = this.use(combineTwoHooks((fast, slow) => computeMiddle(fast, slow), this.fast, this.slow)) // applicative
    
      render() {
        return (
          
            
            
            
          
        )
      }
    }
The trick is functions that convert hooks into new hooks.

Re: Why Do React Hooks Rely on Call Order?

#104

Earlier quoted context omitted.

>From a design standpoint, if `useState(symbol)` is acceptable (and I'm not saying it is), then `useWindowWith(symbol)` would likewise be acceptable in that it's not adding any more requirements to the interface than the vanilla version. I think you're missing that `useWindowWidth()` could have more than one `useState()` and thus you'd need to somehow compose Symbols. Which is what the next section is about.

In particular, I would suggest to take the `useSubscription` example from "diamond problem" section and try to convert that whole snippet. You'll see where it falls apart.

I must be missing something because this didn't fall apart when I refactored it to work with a `useState(symbol, value)` interface. I'm not saying this is a good or ideal solution, indeed if the useWindowWidth hooks were to both useState and also useSubscription, then multiple keys would have to be injected -- it would work correctly, but the interface would have the AMD feel that perhaps is undesirable (per flaw #8).

https://gist.github.com/politician/5f03c169a4119a63abb785b1c...

EDIT: I still think it's a hard design problem and the concerns in flaw #8 are _very real_ to a broad range of developers. When you're just trying to get it done, proper bookkeeping of Symbols and injecting them into hooks composed of other hooks might cross the line. It's a shame that relying on call-order indexing is the solution because it's magical. But at the end of the day, engineering is about trade-offs. Time will tell whether deeply nested composition of automatically-managed hooks was a good feature to expose.

Re: Why Do React Hooks Rely on Call Order?

#105

(I edited the post title to include “React” before the “Hooks” to disambiguate. Might be worth editing the submission too!) Hope you’ll enjoy reading it.

Enjoying your posts, keep them up! Hooks look like a very powerful API for sharing pieces of functionality between components. You've probably heard this one a lot, but what bothers me a bit about hooks (especially useState) is that before, when you saw a component defined as a function, you could assume that it was just a function that renders something based on its props. However, I think it's a fair trade-off for…

Recent versions of React have encouraged truly pure props-only functions to wrap them with React.memo(), which also adds the benefit of giving them the equivalent of a shallow-check `shouldComponentUpdate()` avoiding re-renders when the props are the same.

React.memo() is all the more useful of a signal in the Hooks world.

Re: Why Do React Hooks Rely on Call Order?

#106
Re: 'Flaw #6: We Still Need a Linter' (first example):

Could the 'primitive' hooks (useState, useEffect, etc) walk up `arguments.callee.caller.arguments.callee.caller...` grabbing function names until you hit a React function? Then use the names to create a 'composed' key automatically? It still doesn't solve the problem of a function using the same hook twice in one function, but it might solve the problem of collision across custom hooks.

Example:

    function useCount() {
      const [count, setState] = useState(0)
      return { count, increment: () => setState(count + 1)};
    }

    function useCountPlusOne() {
      const {count: baseCount, increment} = useCount()
      return {count: baseCount + 1, increment}
    }

    function MyHookComponent() {
      const { count, increment } = useCountPlusOne()
      return ...
    }

Would give you a key of `useState(useCount(useCountPlusOne(MyHookComponent)))` without the end user having to futz around composing the key manually. At this point you could probably even forego the 'use*' convention

It's still pretty magical, but the magic seems more abstracted. In general I've really liked hooks, and I'm willing to put up with the wackiness (although testing them with enzyme is a big PITA right now).

Thanks for the article :)

Re: Why Do React Hooks Rely on Call Order?

#107

Re: 'Flaw #6: We Still Need a Linter' (first example): Could the 'primitive' hooks (useState, useEffect, etc) walk up `arguments.callee.caller.arguments.callee.caller...` grabbing function names until you hit a React function? Then use the names to create a 'composed' key automatically? It still doesn't solve the problem of a function using the same hook twice in one function, but it might solve the problem of collis…

There are some big performance implications of using `arguments` (most current JITs heavily deoptimize functions that use `arguments`), and arrow functions in the spec are supposed to throw errors for any attempts to access their `arguments`.

It's probably not a good idea for Production code.

Re: Why Do React Hooks Rely on Call Order?

#108

Earlier quoted context omitted.

In particular, I would suggest to take the `useSubscription` example from "diamond problem" section and try to convert that whole snippet. You'll see where it falls apart.

I must be missing something because this didn't fall apart when I refactored it to work with a `useState(symbol, value)` interface. I'm not saying this is a good or ideal solution, indeed if the useWindowWidth hooks were to both useState and also useSubscription, then multiple keys would have to be injected -- it would work correctly, but the interface would have the AMD feel that perhaps is undesirable (per flaw #8)…

> It's a shame that relying on call-order indexing is the solution because it's magical.

I've been thinking about this a lot since Hooks were introduced, and I'm increasingly of the opinion that it isn't that magical. Order of operations is incredibly important in the functions we write, especially in a language like JS that makes no effort for strict "pure" side-effect free functions. The order of a console.log or a return versus an increment matters in JS. We write a lot of procedural code in JS where order matters (a lot) already.

In that matter, Hooks can just melt into the "procedural" background of JS.

That said, I still feel like I want a better solution than "lint errors" for things like accidental branches of a Hook. I don't have any more of a proposal for how that would work or what that would mean than the article here, though, unfortunately.

Re: Why Do React Hooks Rely on Call Order?

#109

Earlier quoted context omitted.

I must be missing something because this didn't fall apart when I refactored it to work with a `useState(symbol, value)` interface. I'm not saying this is a good or ideal solution, indeed if the useWindowWidth hooks were to both useState and also useSubscription, then multiple keys would have to be injected -- it would work correctly, but the interface would have the AMD feel that perhaps is undesirable (per flaw #8)…

> It's a shame that relying on call-order indexing is the solution because it's magical. I've been thinking about this a lot since Hooks were introduced, and I'm increasingly of the opinion that it isn't that magical. Order of operations is incredibly important in the functions we write, especially in a language like JS that makes no effort for strict "pure" side-effect free functions. The order of a console.log or a…

> I still feel like I want a better solution than "lint errors" for things like accidental branches of a Hook.

The best I can come up with is Sweet.js (hygenic macros), but can you imagine what outrage that would provoke?

Re: Why Do React Hooks Rely on Call Order?

#110
post #94

Earlier quoted context omitted.

I entirely share your "ew, gross, no" re: mixins generally. That their being added to React in the first place is part of this history is definitely interesting. Thing is, I don't even consider Hooks not OO . They're just a really limited in-JS partial re-implementation with bizarre syntax. React tracks your "this" for you so it can dispatch the calls correctly. Your constructor gets mushed around in your render func…

Sorry but this is still pretty hand-wavy :-) I’d be happy to discuss a specific “before” and “after” code example. There’s neither methods nor properties in Hooks code. I think you might be doing the same thing you think we are doing — you’re projecting the API you see onto the metaphors that feel more familiar to you. But these metaphors don’t really match what the API is doing or what it represents. But again, this…

> There’s neither methods nor properties in Hooks code. I think you might be doing the same thing you think we are doing — you’re projecting the API you see onto the metaphors that feel more familiar to you. But these metaphors don’t really match what the API is doing or what it represents.

I mean that Hooks implementation re-implements key elements of a typical implementation of methods and properties, and end up mimicking them in important ways, differing mainly in the parts of that it doesn't include. Not that it actually uses methods and properties (though it does, of course—the "current component" that React tracks is an object, and the Hooks code leans on that to determine its calling context).

> you’re projecting the API you see onto the metaphors that feel more familiar to you.

Place a typical OO implementation next to what Hooks are doing, and you don't even have to squint to see that they're quite close. The porcelain's super-weird, yes—magical in all the "wrong" places, explicit in all the "wrong" places, missing a ton of mostly-inheritance-related stuff—but even with all that a Render function using Hooks manages to look an awful lot like a class declaration, as if someone had implemented classes in a language but forgotten to add any of the syntax to support it.

EDIT: I mean, seriously. The source is public. It's a really fun read.

https://github.com/facebook/react/blob/master/packages/react...

Post reply on HN