5 ms·
Hooks are much worse because they poorly emulate classes. What you need for components is a stateful container, with an initialization phase, a render function
by spankalee 4y ago
Hooks are much worse because they poorly emulate classes.
What you need for components is a stateful container, with an initialization phase, a render function that can be called for each update, and lifecycle methods that are called at lifecycle events.
Classes give you exactly this:
- Constructor for initialization
- Class fields for state
- A method for rendering that can be called multiple times, which can reference state
- Lifecycle methods
Classes are simple, standard, and give you everything you need for UI components. They were practically invented alongside the concept of UI components.
Hooks try to cram all of this into the repeatedly called render method and it's just a failure.
- You have to put initialization into callbacks because the whole component is called multiple times
- It's difficult to manage object and callback identity across renders requiring useCallback() and useState() to fix
- Lifecycle callbacks are put into hook arguments, but they're just trying to be methods and end up recreating the lifecycle closure every render.
- Hooks have restrictive rules on their usage
- Hooks make a function no longer just a function. It needs to be invoked inside a hook context, making it much less portable than a constructor
- Hooks hide their state within the hooks system, making them much harder to introspect and debug.
Hooks supposedly solve this composition problem and allow reusing logic, but that is entirely possible with classes. It's just a huge amount of FUD and misinformation to say that you can't do composition with objects. All you need is an object interface that is similar to the component interface that the component delegates to from its lifecycle methods. This is a simple and standard pattern to use and implement: it's just a single line of code per lifecycle method. And it's easily introspectable with a debugger: just follow object references to the helper objects.
lit.dev does with with reactive controllers: https://lit.dev/docs/composition/controllers/ https://lit.dev/docs/composition/controllers/
It's a very simple alternative to custom hooks, and eliminates the need for all the builtin hooks.
- lmm 4y agoClasses with state are a nightmare. You can never tell what has changed or where. Having the state in the hook system where it's visible to your tools and you can actually see what's changed between one render and the next is a huge win.
- gnaritas99 4y ago[dead]
- spankalee 4y agoYou can do that with classes too though. We use decorators to make class fields reactive and track what changed between renders.
- dylanowen 4y ago100% this. Reading complex functional components is challenging because you're creating the functionality of a class with non of the organization.
- codeptualize 4y agoHow do you mean? Imo it's easier to organize as you can split up your logic into normal functions and custom hooks (which are also just functions). I also find functions much easier to read as there is no "this" and you can simply read them top to bottom for order of execution.
- dylanowen 4y agoSo I spent some more time reading through using HOCs in React and I think I'll have to give functional components more of a try. The specific example I was thinking of is https://github.com/graphql/graphiql/blob/50674292c55eadf0e61993ffbbc7a6983c608bf4/packages/graphiql-react/src/editor/response-editor.tsx#L37 https://github.com/graphql/graphiql/blob/50674292c55eadf0e61... but this component will probably always be complex because of the Codemirror interaction.
- codeptualize 4y agoNice! I think you might like it. Ah yeah, that does take a while to unpack. I think a lot of the complexity there is dealing with a non-react library and the dynamic import(s). Binding non-react libraries can be a bit rough. I do think it's a good example to show the big advantage of hooks, if you look at the use of the hook, super clean: https://github.com/graphql/graphiql/blob/50674292c55eadf0e61993ffbbc7a6983c608bf4/packages/graphiql-react/src/editor/components/response-editor.tsx#L9 https://github.com/graphql/graphiql/blob/50674292c55eadf0e61... Great way to contain complexity and make usage really clean and simple!
- bayesian_horse 4y agoReact functional components are not supposed to be complex. It's easier to split FCs up than in Vue for example. Even React's class based components discourage splitting components by adding more boilerplate.
- robertoandred 4y agoSounds like you should stop trying to think of hooks as classes. They're not.
- gherkinnn 4y agoI don't know where your downvotes come from. It's a different paradigm, and twisting hooks to behave like classes clearly doesn't work. I partially blame it on React not properly updating their hooks docs in time (they're 2.5y late, ffs). Hooks are the way to go, class components should have died a fiery death back in 2018, and at this point any other opinion is just wrong.
- KronisLV 4y ago> Hooks are the way to go, class components should have died a fiery death back in 2018, and at this point any other opinion is just wrong. To your snarky comment, I'll raise my own snarky rant from a while ago, about hooks leading to render loops that are impossible to debug: https://blog.kronis.dev/everything%20is%20broken/modern-react-is-broken https://blog.kronis.dev/everything%20is%20broken/modern-reac... It's probably not the kind of writing that I'd strive for nowadays but the error message not telling me exactly where the problem is felt unacceptable and clicking on the error source leading me to React code instead of the problematic lines in my code felt just unnecessarily weird. I know that hooks are touted as the best way to extract reusable bits of code, however when multiple useEffect hooks have issues with dependency arrays and start causing render loops, clearly something is wrong. Personally, I acknowledge that class based components are an easier mental model for many out there, but are also dead due to the direction that React is heading in. But I would also suggest that the new Vue Composition API does hooks better than React and that using Vue with Pinia (instead of React with Redux) will overcomplicate things way less for developers who just want to get things done.
- codeptualize 4y agoSkimmed over your article. The infinite loop situation is not exclusive to hooks and very much exists in classes. The situation is calling setState from useEffect, which triggers a render, triggers the setState, render, etc... If you setState in componentDidUpdate without any guards you will end up with the same loop. I don't remember how debugging this worked, but I'm pretty sure it wasn't pretty. As to big messy components: I don't think you can blame that on hooks. Hooks make it easier to split all of that up so you have isolated well contained chunks of logic that are much easier to test, debug, and manage. Vue; not super familiar but I believe they use reactivity right? I think reactivity can be nice, but is also not as great as it's made out to be as it gets tricky as soon as you need to take control and prevent extra renders etc. A lot of it is making diffing and memoization less explicit, which can be nice, but also has some tradeoffs.
- oaxacaoaxaca 4y agoTHANK YOU. Very well said. I've been annoyed by hooks since they came out and you described my sentiments perfectly.
- orange8 4y agoThis kind of reminded me of the module pattern [1] popular with ye olde jQuery plugins. Just realized that hooks try to emulate what JS does natively, which is keeping track of all local variables in a functions calling scope. Meaning that if that function returns an object, that Object still has access to all the variables declared when that function run. [1] https://stackoverflow.com/questions/17776940/javascript-module-pattern-with-example https://stackoverflow.com/questions/17776940/javascript-modu...
- zarzavat 4y agoWould generator functions help? function* Counter(props) { const [state, setState] = makeState(0); const incr = () => setState(state.value + 1) while (true) { yield <div onclick={incr}>{ state.value }</div> } }
- branko_d 4y agoMost importantly: hooks make functions that look pure actually impure. Before hooks, if you wanted to call a functional React component, you could reasonably assume it's pure - it depends just on props and nothing else. Only when trying to use a class component did you needed to start to worry about it's internal state and how the lifecycle influences it. With hooks, you are never sure until you look into the function's implementation!
- mejutoco 4y agoExactly! I could not put it better. All the talk about side-effects. Hooks make side effects more confusing, not less, for the reasons you perfectly explained. Small rant ahead. Disclaimer: I find React very useful, just not this constant churn of approaches. One solution would be to make all state pass through a single point (like Elm or Redux) but some people do not like this because it "limits" them. Limiting side-effects lowers cognitive load, same as Type systems are usually not Turing-complete. Limits can be good. Same with Context, which is practical because you can write things from anywhere. Congratulations, we were concerned about side-effects and now we are basically using a global variable. It is like some people talk about side-effects and functional programming and they think it involves using map and filter and done and they don't think deeper about what it means, and see no contradiction in it where it contradicts the principles they are promoting.
- wildrhythms 4y agoAll true. I work on a massive React codebase and it is a challenge to follow the logic because at any given time there could be a dozen effects firing off from a single state change. Granted, having the dependency array helps follow what's firing off, but when effects kick off other effects it gets into a mess. (Probably a sign that we should refactor some things) This problem isn't solved by class-based components either; years ago when I first started working on the codebase I found class-based components with gigantic componentDidUpdate() full of hard to follow conditionals and setting state. That said, hooks makes it easier to refactor problems like this in my experience.
- rimunroe 4y ago
- codeptualize 4y agoIt’s funny that controllers are conceptually not that different from hooks just with classes. Question: can controllers access everything on the host? As that would easily turn into a giant mess. Probably you shouldn’t but isn’t that the same as hooks rules? Classes have lots of rules and best practices to prevent a mess, just like hooks. Also stateful classes are notoriously hard to debug as everything can change everything all the time. I think you also should try hooks for a bit. Some of your statements about them are incorrect or really not that prevalent in normal use, you might be surprised how nice they can be. Especially use react-query or swr for fetching and something like zustand for state.