- said it was bad
- not provided any concrete details
> they've become unmaintainable when reaching a certain size
How?
Did you start getting lot of hard to replicate bugs?
Did you find authoring new components was made difficult by your heavy use of custom hooks that were actually not as generic as you thought when you wrote them?
Did you find 3rd-party hooks had bugs?
Did you find that useRef doesn't work like you think it does, and you can't mix and match useRef and useEffect? (ouch)
What kind components are you writing that you find hooks are such a bad fit for?
Be specific.
(I'm not saying hooks are perfect; I've had all of the issues I listed happen to me, but it's still quicker to implement new features on a medium sized web app using hooks than with components in my experience; I work on 3 apps, and only one of them still uses components, and it's the most annoying to make changes too, simply because for simple business components & forms, hooks seem to reduce the amount of boiler plate, and flat out, reduce the lines of code; less code -> faster changes. It's not always that simple (see bugs comment above), but mostly... it is, in my experience: ...HOWEVER, what you've done is just wave your hands vaguely and fail to actually say anything except you don't like hooks)
One of my favorite papers on it: https://github.com/papers-we-love/papers-we-love/blob/master...
How complex it is is not relevant unless it has a tangible impact.
eg. I use spark a lot. I don't really deeply understand how the DAG is translated into a distributed computation and the results are aggregated from the cluster.
It calculates things. It's fast. Sometimes I'm surprised by things that are slower than I expect. Oh well. Don't do lots of column renames. /shrug
It's a tradeoff.
Is the effort to understand the detail worth the benefit of doing so? Does not understanding it cause enough pain that it become prohibitive to develop using it?
Is it for spark? No. I really don't care how it's implemented. I don't even know scala. It works fine.
Is it for assembly? No. I don't care at all how my code is JIT'd to assembly / code. It just works.
It it for react hooks? I honestly haven't personally found it to be... but I don't write custom hooks much.
So, your experience with hooks might be different, and you may find the trade-off is more expensive if your case, because it tangibly causes, eg. bugs when you write your code.
...but you are quite wrong if you think that not understanding how something works is a fundamental obstacle to using it.
That is categorically false.
I would argue that it's far from proven that using complex systems necessarily causes the complexity of your system to balloon out of control... or that there is even a strong casual relationship between "mental model being complex to grasp" and the resulting complexity of the system.
> ...it's far from proven that using complex systems necessarily causes the complexity of your system to balloon...
I don't know what "proven" would actually mean in this context, but there's a notion called "leaky abstraction". It bites you hard when you use a "do-everything" framework and something goes wrong.All of sudden, the magic stops and you have to deal with a ream of obtuse stack traces, problems that completely smash your metal model of what's going on, and you end up in deep rabbit holes of stuff you don't want to get into at the worst possible time. That is a fundamental obstacle.
Thats the point: the mental model is irrelevant unless it causes bugs when you use it wrong.
Does it? Does it actually cause bugs?
Not, “in general”; You’re just doing the same thing again here and doing vague hand waving. What actual bugs? What types of bugs?
Be specific
(yes, it does cause bugs, but see how this conversation is pointless when you don’t provide any details? Right, now go read the first post in this thread.)
I've read a lot warnings about the pitfalls, and about hooks not doing what you think they are doing, but honestly, I haven't experience any of that, maybe because my use cases are so close to the documented ones, that I don't need to stretch the concept, and it just works as intended for me.
Could it be, that as with many other programming patterns, some developers are using it as a silver bullet, and trying to solve everything with hooks?
Otherwise I'm failing to understand, it would be useful to see examples as the mentioned above.
> Should I use Hooks, classes, or a mix of both? [0]
> When you’re ready, we’d encourage you to start trying Hooks in new components you write. [snip]
> Do Hooks cover all use cases for classes? [1]
> Our goal is for Hooks to cover all use cases for classes as soon as possible. [snip]
The answer about higher-order components is a little more nuanced but does say hooks should replace most of them:
> Do Hooks replace render props and higher-order components? [2]
> Often, render props and higher-order components render only a single child. We think Hooks are a simpler way to serve this use case. There is still a place for both patterns (for example, a virtual scroller component might have a renderItem prop, or a visual container component might have its own DOM structure). But in most cases, Hooks will be sufficient and can help reduce nesting in your tree.
Learning enough about the React lifecycle to make simple apps was a big task, but it felt like a bounded one for my purposes. Learnings hooks seems... strangely open-ended. There's a small set of built-in hooks that do certain things, and if I need more I'm supposed to build new hooks out of the old ones, and that's supposed to be it. Except that it isn't, because if that's all there was to it, people wouldn't be having difficulty. I just wish the rest of the story was sketched out and bounded for me in some way so I could know what I was getting into.
[0] https://reactjs.org/docs/hooks-faq.html#should-i-use-hooks-classes-or-a-mix-of-both
[1] https://reactjs.org/docs/hooks-faq.html#do-hooks-cover-all-use-cases-for-classes
[2] https://reactjs.org/docs/hooks-faq.html#do-hooks-replace-render-props-and-higher-order-componentsI remember people having difficulty with callbacks, then with generators, then with promises, Obersavables... you get the point. There is always something new that tries to make 80% of the cases trivial with a new elegant syntax that hides a lot of complexity, and comes with that 20% of cases where it is harder to grasp.
On the one hand, they are nicely composable. You can make one hook call depend on another and you can easily build a lot of cool functionality thanks to this. On the other hand, you are no longer able to directly follow the flow of the code. Making a change to a series of hook calls can sometimes be scary to me as it can be hard to fully understand the cause-and-effect of that change.
On the one hand, you can hide a lot of complexity behind a simple `useSomething()` call, on the other hand the code inside `useSomething()` can be absolute horror, because all the stateful logic is handled through hooks. Any non-obvious use-case ends up being a mess of hook calls. If you only write the code once and never need to touch it again, `useSomething()` can be an amazing hook though. There are some hooks that I have written that I hope I (or anyone else) don't ever have to touch. I might just be a bad programmer though, or missing some obvious patterns.
TLDR: hooks work well, but they have downsides in terms of maintenance burden
Hooks are, at least as they're presented on the surface level, exceptionally functional, so shouldnt it be easier to directly follow the code? Just follow the function calls?
If you need to make such statements something is very wrong. Either your code or with the design patterns you're forced into by your framework/library
With hooks however, you can create self-contained custom hooks that group all the code of a feature in one separate file. It's much easier to test and to make sense of. It's also easier to make the hook more generic and re-usable when it becomes needed by other components.
I'm definitely not going back to class-based components as I find React hooks a lot easier to manage and maintain.
Sometimes when I read these comments I feel like I am using a completely different library.
They'll say something and I'm wondering "what do you mean exactly..." but we don't know.
I get how folks might not want to get into the weeds on minutia or etc. But without examples it's hard not to think someone just doesn't 'get it' too.
I'd much prefer the latter, but it seems that links are the way of websites such as these.
In fact the linter explains exactly that in a paragraph:
> React Hook "useState" may be executed more than once. Possibly because it is called in a loop. React Hooks must be called in the exact same order in every component render.
Why would you call a hook inside a loop anyway? Hooks return callbacks and variables, which you can use inside a loop without problem.
If you push me by asking "why?", I can expand that sentence by including "because each hook call is associated with an index under the hood".
This is probably the only React hook nuance you really have to understand, and it's not particularly difficult.
Absolutely not. There is the dependencies array, how it only does a strict equals check (no shallow comparison), how you need to memoize other dependencies so they don't change on every render, why it is a problem to leave it empty, why useEffect cannot strictly replace componentDidMount, why you can't put one hook inside the other, how (and which) hooks can trigger a re-render or block updates, how state is captured by the closure and what to do if you need current state, how to persist things across renders using useRef.. and probably more.
So basic memoisation?
> why you can't put one hook inside the other
Hooks inside other functions may be called at any time, how is React to know which component is calling them?
No. Before you just had to worry about shouldComponentUpdate and data received from the 'outside'. With hooks you need to memoize arguments to every function you call within the component - you didn't have to memoize class methods. The concerns and patterns involved are very different.
Edit: That was snarky, I apologise.
2. But it runs a bit differently sometimes: const [a,setA] = useState(1234); Here "a" will have the value 1234 on the first run (what is a first run? When react instantiates this component? When it mounts?) Then this same line of code will assign a different value to this same variable, because setA was called in the past. Why? How does it know that this exact local variable needs a different value?
Render functions are only called when a component is being rendered. React keeps track of which component is currently being rendered. When you call `useState`, React knows which component is currently being rendered and keeps track behind the scenes which state that component has.
The way it knows which value it should return for `a` ties directly in with why you need to call `useState` calls in the same order -- React basically keeps a list of the state behind the scenes, and returns the (secret behind the scenes) state in order. If you change around the order of `useState` calls, it can't use the ordering of the calls to determine which state needs to be returned.
<div> {isLoggedIn ? <LogoutButton /> : abcd } </div>
and I change the value of isLoggedIn. Does the state reset? I'm not asking for a solution, just pointing out that even a 'simple' hook like useState has its small quirks.
This is the same behavior as has always existed with class components and `this.setState()`. Component-scoped state only exists as long as that specific instance of the component is mounted.
2. Before calling your render function, it stores a reference to that object in a private, global variable. (Global in the sense that there's only one per JS VM) (Javascript is single-threaded so this is perfectly safe to do).
3. Each of the built-in hooks has access to that variable, and use it to increment a number representing the current hook index and then store information in an array at the current index.
4. The next time your component is rendered, that information is still there and the hooks can retrieve it.
5. Also some hooks like useEffect set up functions to be called by react later.
const React = /* Implement this */
function HelloComponent(){
const [myNumber, setMyNumber] = React.useState(0);
// Simulate rendering data to a screen
console.log("My number:", myNumber);
// Simulate rendering a clickable button
const click = ()=> setMyNumber((num)=> num + 1);
return click;
}
React.mount(HelloComponent); // prints 0
React.simulateClick(HelloComponent); // prints 1
React.simulateClick(HelloComponent); // prints 2
React.simulateClick(HelloComponent); // prints 3
General idea being that the React item may be stateful, but the HelloComponent must be a stateless pure function. How can it draw on React's statefulness, while only using that useState() call?I am working through this exercise now, but I'm already finding myself doing some pretty hacky things to make this work (like finding out who called a function using arguments.callee.caller.name). Still, it's pretty interesting.
Edit: My solution to this: https://gist.github.com/rashkov/f765917d09ebd629f385c21195f4...
I was mainly interested in solving for how React lines up the useState calls with the actual data that it's keeping under the hood. So I definitely cheesed through the parts touching on how React keeps track of the component tree. I'm enjoying reading through your interpretation of this though. Thanks
There's nothing stopping someone from reading the explanation in the React docs[0] if they want to understand.
1) React solution will need a little bit more lines;
2) There are multiple ways to write that and some ways are less efficient than others;
3) React solution is harder to write if you only started with hooks.
I'm going to use React because:
1) I don't need to learn anything new (only hooks) to do what I need;
2) I have richer environment: typescript, testing libraries and all React libraries/solutions I might need. Svelte is getting better and potentially it already covers things I need but that was not the case one year ago.
In fact, it smells like the author doesn't know what he's talking about; in my experience "That will break for the following incredibly subtle reasons:" is usually about React fundamentals such as being deliberate with state mutations.
Custom hooks are some of the most useful and fun code to write, in my experience.
If anything, React is becoming less of a black box, with a 'simpler' API. It's just that in the process to moving to or learning the new API, people are understanding they didn't actually understand React before. They're now being made to confront that.
You can disagree with him fine, but I'm quite sure the founder of Formula knows what he's talking about when it comes to React.
I've only touched that package a few times since but it never fails to make me wish the original lead hadn't used Formik. I dunno, forms in react have never given me tears, but contrary to Formik's slogan it has been painful, even aside from this versioning fiasco, so I'm not sure this endorsement is as strong as you think it may be.
What I'm saying is silly is to say the author is somehow "uninformed" when it comes to React like he's some sort of newbie, as opposed to the guy who wrote the library that a sizable percentage of React apps use for their form components.
The author is Jared Palmer. He's a well-known engineer who has worked with React for years, and the author of a very popular OSS React library (Formik).
I used Formik long before hooks, then later, after hooks. I remember hesitating a few times because some of the exceedingly neat pattern/syntaxes were impacted negatively by hooks.
I still "happy" with both, but my team is all quite senior and I feel for jnr teams trying to grapple with React AND hooks at the same time.