The developer isn't just "expected to know", because they'll get an error if they don't. React's debugger is really good at pointing out mistakes.
As someone who started off thinking hooks were too "magic" to be taken seriously, such concerns are a thing of the past.
If you just think of calls to useState as variable declarations, it makes zero sense that you'd ever want to call useState in a different order.
this -
let userName = 'bob';
let favouriteColour = 'purple';
is this
let [ userName, setUserName ] = useState('bob)';
let [ favouriteColour , setFavouriteColour ] = useState('purple)';
Hooks enable you to reason about the state your component is in in a way that class components couldn't.
Hooks felt extremely awkward to me at first, because it was forcing me to write code in ways that felt unnatural. Once I stopped fighting it, the benefits became apparent, and now the old way feels awkward.
actually works? How does `useState` know what bit of state to grab? (it's a global store identified by the function you're in, and an index. it's why you can't change the order of hooks nor call them conditionally).
Hooks are literally javascript in the sense that they're functions you can call in javascript, but they don't operate under any of the rules people actually think of when they write javascript. If for any `function x() {}`, `if (condition) { x() }` was a fatal error, people would consider that terrible API design and the function shouldn't need to care whether it's called conditionally.
Sure, react hooks 'compose' better than class components, but they also don't compose *at all* with anyones mental model of how JS works. What they needed was different syntax or a DSL in general (what svelte does is a lot more understandable, imo).
[0]: jsx is a DSL but it's merely one bit of syntax sugar. they needed a hell of a lot more.
In practice, because they don't work the way most people think they work, and because it's really easy to screw up using them.
Hooks are called every render, which is a relatively transparent process that happens all of the time. All hooks have to be called every render, otherwise you're breaking the rules of hooks. The reason why is because the only way hooks know which hook they even are, is the order in which you call them.
For example, if I have
const a = useRef(true);
const b = useRef(false);
The only way react knows that the first value it should return the next time it renders is what I'm expecting to be value `a` is because that useRef is called first every render. These are all kinds of rules and assumptions about hooks that make them not behave at all like normal functions. I don't think people understand that, for useRef, for example, those two lines of code are run every render, passing in the initial starting values, which then react disregards after the initial render, and maintains a mapping of ref and state and memo etc values all by the order the hooks are called in. I see people use hooks in callbacks all of the time and just fundamentally not understand that they're doing it wrong.
Then there are all of the closure problems with useEffect and useCallback that I see people really struggle with.
And useState...lordie. The fact that it defers setting the state value, whereas useRef will immediately have its value updated. Deferring useState has caused so many bugs.
I think people are getting too hung up on the "implicit ordering" thing. The only thing that really matters about the ordering of hooks is that the call pattern remains stable between render passes. This code:
Are you referring to hooks in general, or the specific useState hook they showed?
I love the idea of hooks - the more I can isolate my logic, the better. The useState example I'm pretty ambivalent about, for the exact reason you state.
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 such a powerful API.
First of all: useState is not a pure function, nor is setState. I would think that’s obvious from the name. The order isn’t hidden and it’s not magic, it’s the order they’re defined in the function body, because under the hood the React runtime is storing all the hooks you call in an array and firing them off by iterating through the array. It is a very conventional architecture in event handlers: of course there is going to be an order they get called in, there is no way to avoid it. But it’s perfectly explicit.
Like I really don’t know how to explain this any clearer.
* React stores the state for your hooks in a list which is iterable.
* useBlah is just a normal function that internally calls next() on that list to get the next hook state which is why order matters.
* React checks that the iteratior on the list is at the end to see if you’ve consumed all the hooks and throws an error if not because it’s indicative of a bug in your code.
Like at this point the only thing I can recommend is try writing a toy implementation of hooks and see that they’re not magic, they’re just normal JS, and that the restrictions flow naturally from the implementation.
Like why do you want so bad for hooks to be weird?
> React knows which component is rendering at any point in time — so it knows which component useState() call corresponds to.
Personally, I think that may be the piece that makes it feel a bit magical. With `this.setState()`, usage of `this` makes me feel like I know how the component and the state are linked.
With hooks, however, there's no obvious link to the component in the code. I'm grabbing `useState` off of the shared react module, `useState` is not passed into the component, and I don't have to reference the component itself at any point while using hooks. The new sets of rules you have to follow to make things line up correctly play a part, as well.
Of course that may be how `this.setState()` does it anyways - you know better than I do, obviously. Maybe the usage of `this.setState()` made me feel confident in something I didn't actually understand under the hood. But at least for me, that's why `useState()` feels a bit magical at first glance compared to `this.setState()`. Not _too_ magical, and not enough to scare me away from using hooks, but still a bit.
Hooks elucidate everything I've felt wrong about React, but have not been able to put my finger on it until recently.
Hooks reveal two major things with React:
1) React developers did not understand the component paradigm that they originally went with. If they did, then they would understand how silly it is that components cannot reuse logic. This was an entire debate many years ago. Composition vs. inheritance. You don't need to throw out classes or objects to get reuse.
2) React developers do not understand functional programming. I could write an entire essay on this. But it should suffice to say, FUNCTIONS DO NOT HAVE STATE. What React hooks introduce has more in common with dynamic scoping, of older LISPs. It fundamentally breaks lexical scoping, which is incredibly important for understanding code flow. You have to be constantly aware of when it's safe to use certain variables and the implications of using variables in certain scopes now. In 2020!! This is INSANE.
> Stateful "functions" is just so asinine I don't even know where to begin
I feel like the React team did a bad job at communicating that the functions are still stateless. The functions themselves never hold state, they just access the state that react holds for each component through hooks.
I think this confusion is further fueled by the fact that most people feel
render () {
return <Button />
}
calls the `Button` function inside `render`, but what it actually does is that it registers the `Button` function in React, telling it "here's how you can create what I want", it doesn't actually call the function. The actual call is done by React, if and when it wants to.
Hooks extends that idea by letting you say "Here's how I want to update my component".
The component functions you write in React are a "description" telling React how to create that component. With hooks you can now "describe" how to update that component with new changes. I feel like hooks force you to let go of the control you thought you had with class-based components. Whether that's a good thing is up upto you.
Conceptually Hooks are modeled as "algebraic effects" feature from languages like Eff and Koka. Those are essentially like resumable exceptions.
With that conceptual model, `useState` is like a `throw` that goes back into React "up the stack", and then goes back into your code.
Of course that's not how it works for perf and other reasons (there's no resumable exceptions in JS). But this may help the conceptual model around how we know which component it is.
I'd argue that hooks didn't really make anything better, they just replaced the pitfalls with different obtuse pitfalls and unergonomic solutions.
Hooks have their share of weird issues; they explode if they're called in a different order/number from how they were called the first time the component rendered (making them not at all pure functions), and it's super easy to screw up the dependency array to useMemo/useCallback (so either your hook is useless and re-installs the event handler on every render, or you get stupid "the callback had a stale copy of the state" bugs not possible with class-components).
I've also seen my fair share of situations where what would have been a this.setState with a callback becomes a tangled mess of useStates and useEffects, or someone bends over backwards to try and avoid re-installing an event handler every time one of the props or state variables it uses changes.
I will agree that hooks are a sin against all that is good and proper. Stateful "functions" is just so asinine I don't even know where to begin. It's like they wanted to ride the hype of functional programming, but didn't understand referential transparency. And somehow that is supposed to be better than classes which, by design, are intended to hold state.
Which brings me to where I disagree with you that React team has their act together. If anything, the hooks API is tacit acknowledgement that they messed up on component lifecycles and their original component model and really don't know what they are doing. If you go back even a year earlier, most people seem to have forgotten that they redid their entire set of lifecycle methods before scraping it all for hooks. They haven't been able to get a stable API together because, architecturally-speaking, they don't know what they are doing.
The only alternative explanation is that hooks is the React team trying to remain relevant through a misguided API "refresh". Which is definitely not a good look for anyone wanting to take a framework seriously. Outside of Facebook, literally no one has the money to rewrite their app every year. Hooks aren't even backwards compatible. It's not even self-compatible with its current version! Since you can't mix hooks and classes. Just insane.
After using hooks for a while, I have to disagree with most of your points. The issue is not clarity or simplicity, it's familiarity. Once you start actually using hooks, things make a ton of sense. It's a different way to think about components, not an inherently more-complex way.
The order of hooks is only important insofar as they do not change between rendering. That's it. Hooks should always be called, and always in the same order.
A basic React Hooks component has way less boilerplate involved than a class component does, and hooks allow for much better code architecture when it comes to separation of concerns.
I don’t think I’ve ever seen anybody claim that hooks are ‘functional’
The fact that each time you call useState in a render method you get back a different value should be a clue.
You have to have a fairly sophisticated/twisted mental model of a react component’s function signature as effectively including each of the hooks it requires to be able to mentally model hooks as pure functions.
Hooks are a clever imperative hack that works only if you stay in the strict subset of JavaScript behavior that react expects, but which lets you get the benefits of some algebraic-effect-like contextual handling that would be easily expressed in a functional syntax, but they’re not functional.
Mind you, an OO person might equally say they are an imperative hack to get you the benefits of dependency injection that would be more easily expressed in an OO syntax, but they’re not OO.
What hooks are is more ‘imperative reactive’, I would suggest.
As someone who started off thinking hooks were too "magic" to be taken seriously, such concerns are a thing of the past.
If you just think of calls to useState as variable declarations, it makes zero sense that you'd ever want to call useState in a different order.
this -
is this Hooks enable you to reason about the state your component is in in a way that class components couldn't.Hooks felt extremely awkward to me at first, because it was forcing me to write code in ways that felt unnatural. Once I stopped fighting it, the benefits became apparent, and now the old way feels awkward.
Hooks are to React what React was to jQuery.
reply