6 ms·
Interesting. I have the opposite experience, the more I use them the more clunky they feel. * If there’s only useState, then everything is mostly fine. All is
by judofyr 5y ago
Interesting. I have the opposite experience, the more I use them the more clunky they feel.
* If there’s only useState, then everything is mostly fine. All is good.
* To avoid unnecessary re-rendering you’ll have to move callbacks into useCallback. These callbacks then also need to specify all of their dependencies. So many lines of code that are merely noise.
* They encourage having state locally in the component which often breaks down the moment you slightly tweak the design. So much refactoring!
* Dealing with any other (callback-based, stateful) API is confusing. If you want something to execute once initially you can use useEffect with an empty dependency list, but note that then there’s no way of accessing updates values (in callbacks) and everything will reflect the initial state. Often you’ll have to use useRef just to keep track of the current state. Or maybe useState?
* Debugging is painful. You forget one value in your dependency list and the weirdest things happen. Suddenly you have callbacks running in different rendering scopes with different values.
They feel very “brittle”: Once it works the code looks pretty, but when there’s a slight change of requirements you need to rethink everything.
- deergomoo 5y ago> To avoid unnecessary re-rendering you’ll have to move callbacks into useCallback. These callbacks then also need to specify all of their dependencies. So many lines of code that are merely noise. I really like Vue 3’s Composition API as a much easier-to-use (IMO) implementation of hooks. It’s conceptually very similar to React Hooks, but the method that defines them only runs once, when the component is instantiated. So straight away that removes any worries around conditionals and loops. Instead of a plain value/object and an update function, the “hooks” instead return a reactive object that tracks it’s own dependencies (which are also all reactive objects). That removes any need to manage dependencies yourself, or for any memoization. The only downside is that because a plain value (like a number or something) cannot be reactive, it has to be wrapped in an object and accessed via a “.value” property, but this is only relevant in the setup function. Anywhere else, like the Options API or in the template, accessing the value like a value will just get transparently proxied to the object.
- nightski 5y agoThe entire concept of reactive objects brings back memories of extreme pain from KnockoutJS days (and terrible performance in complex scenarios to boot). I prefer just working with plain old data structures.
- benatkin 5y agoI prefer working with just plain old setup functions, that are called once.
- bobthebuilders 5y agoI find React and Vue to be different expressions of the same core concept in a way. Althoguh I do prefer the functional nature of React, reactive objects are a lot less boilerplate, even if it might be a bit slower. The composition api itself is actually I think where it shines, because the options API is very mediocre in my opinion.
- yurishimo 5y agoIs it actually slower though? I feel like that’s Vue’s entire marketing strategy. “We’re just as fast and small as React but include way more out of the box and it’s easier to grok for new people”.
- wayneftw 5y ago> You forget one value in your dependency list and the weirdest things happen. Don't you get eslint warnings right in your editor? If you're using VS Code there's an extension for that...
- Waterluvian 5y agoNot only this but I need the freedom to control the dependency list for an effect. Sometimes it isn't as simple as "every variable being referenced inside the effect."
- MispelledToyota 5y agothat's a bit confusing. While you can sometimes get away with leaving out dependencies, and then you don't have to useMemo or useCallback as often, it seems like there is a much larger downside to having potentially stale values in your effect. Leaving them out to me would result in the potential for weird future bugs and race conditions.
- mrighele 5y ago> If you want something to execute once initially you can use useEffect with an empty dependency list, but note that then there’s no way of accessing updates values (in callbacks) and everything will reflect the initial state. Often you’ll have to use useRef just to keep track of the current state. Or maybe useState? I'm not sure if I understood what you mean, but note that useState accepts not only the updated state, but also a function that takes the current state as argument and return the updated state. In other words, instead of something like this useEffect( async () => { ... const someValue = await someFunction(); setState({...state, someValue }); }, [... ] ); you should use something like this useEffect( async () => { ... const someValue = await someFunction(); setState( currentState => ({...currentState, someValue })); }, [ ... ] ); (the example is with async but it is the same for regular callbacks)
- ricardobayes 5y agoYou can use a boolean ref for this in the useEffect to keep track whether it was the first render or not. https://reactjs.org/docs/hooks-faq.html#can-i-run-an-effect-only-on-updates https://reactjs.org/docs/hooks-faq.html#can-i-run-an-effect-...
- jakelazaroff 5y agoNote that you shouldn’t (can’t?) actually pass an async function directly to useEffect, since it returns a promise: https://github.com/facebook/react/issues/14326 https://github.com/facebook/react/issues/14326
- jacobedawson 5y agoNot true, see Gaeron's direct reply in the thread you linked to, that's been the standard for 3 years and works like a charm - useEffect specifically is the hook for handling async functions ("impure effects").
- azangru 5y agoNotice the words "pass to useEffect" in the parent comment, and the code snippet in the grandparent they are commenting on. The type of the callback function that useEffect accepts is () => void rather than () => Promise<unknown>. What's not true about that?
- mikojan 5y agoSeconded. Plus, you start writing wrapper functions for standard Javascript APIs (e. G. window.setInterval) to force them into Reacts data model.
- jameshart 5y ago... which is a good idea anyway because you want your code to be testable.
- mikojan 5y ago...this makes no sense at all.
- mikojan 5y agoWhy would you be down voting this? Parent's statement is completely nonsensical. If you wanted to test a function irrespective of window.setInterval's implementation you would just PASS window.setInterval as a parameter to that function. You would NOT add a wrapper function for window.setInterval because it is completely useless.
- jameshart 5y agoThe function you’re talking about would be a react functional component. I don’t think passing window.setInterval in as a prop to a react component is a particularly common approach to achieving component testability - though by all means give it a try. I don’t think it’s ‘nonsensical’ though to suggest that it’s a fairly common practice in testing to not want your unit tests to actually set up recurring callbacks. Isolating an external side effect to make it testable, as well as to align its abstraction with the one that your framework uses is a common pattern anyway. I’m not sure why this angers you so much?
- erikpukinskis 5y agoWhy would you do that? What’s wrong with this? useEffect(() => { const interval = setInterval(myFunc, 1000) return () => clearInterval(myInterval) },[]) I think one of the keys to React is not to fear the boilerplate. If you find yourself typing the same code too often, there’s probably a higher level abstraction that you should find, way above the level of setInterval. I find coders often try to DRY up low level code to the detriment of actually dealing with their _application_ architecture. In general, you should use the tools as provided, boilerplate and all, and then focus your real brain energies on the specific domain problems that only your application needs. Creating thin wrappers on external tools to squeeze the last tiny bits of DRYness is not only usually a waste of time, but it makes your code unreadable. People know how setInterval and useEffect work. If I see useInterval I have to go read your code to know what’s happening. Especially if you made it configurable. And people _will_ tend to add configuration to these wrappers over time as needs change.
- jakelazaroff 5y agoMy experience is mixed but mostly positive. - Agreed about useState (and useReducer). They’re simple and they get the job done. - Agreed about useCallback as well, it’s a lot of boilerplate. - State in general is a hard problem to deal with, but I’m not sure how hooks in particular encourage it? Class components had the same issue. - IMO, useEffect is a huge footgun. I understand how it works, I understand how closures work, but it is by far the biggest source of hook-related bugs I’ve experienced. - This eslint plug-in will solve your dependency list woes: https://www.npmjs.com/package/eslint-plugin-react-hooks https://www.npmjs.com/package/eslint-plugin-react-hooks
- ketzo 5y ago+1 for that eslint plugin, I was just idly wondering if something like that existed earlier this week. Thanks.
- jakelazaroff 5y agoUnless your app is peculiar in some way I’d recommend using something like Create React App, which includes it automatically. https://create-react-app.dev https://create-react-app.dev
- simion314 5y ago>You forget one value in your dependency list and the weirdest things happen. This is what I hate in angular1 , there are at least 3-4 ways you can mess up a simple data-binding and there is no error or warning to inform you that something is not right so you waste a lot of time to figure it out. I used react a few years back but it seems that this days it got hyper complex.
- bobthepanda 5y agoAs far as I can tell you can just not use hooks, if you (and the rest of your contributors) agree.
- deleted 5y ago[deleted]
- schwartzworld 5y agoRegarding useCallback, you can probably skip it in most cases, unless the extra renders are a problem for you. Linters should help with missing dependencies.
- ng12 5y agoYes! useCallback is an optimization and using it prematurely can be problematic.
- grandpoobah 5y ago> You forget one value in your dependency list and the weirdest things happen Really? I've been working with hooks for months, with code in production, and I hardly ever pass more than one or two items to the dependency list. The only time I really had any trouble with dependencies was when I was updating appContext inside a hook.
- jimrandomh 5y ago> You forget one value in your dependency list and the weirdest things happen. There's an eslint plugin specifically for detecting this, which should probably be considered mandatory.
- phailhaus 5y ago> Debugging is painful. You forget one value in your dependency list and the weirdest things happen This should never happen. The only downside to hooks is that the eslint plugin is _required_, not optional. It is not worth the pain without it, but you'll be in a Whole New World once you do it. Since you can auto-fix every time, it'll become second nature and you'll never forget a dependency again. You can focus on getting your application to work.
- joshribakoff 5y agoYou can use OOP or whatever you want to build your abstraction and then surface that as a hook, rather than using hooks as a way to do all your logic. This is similar to how there are npm packages that just import some other js package “foo”, wrap its api in a react component, and reexport it as react-foo I’m curious what you think of https://rxstore.dev/ https://rxstore.dev/ it basically is a pattern I tried to outline where you write logic with rxjs and it tries to bridge the gap to react/hooks for you. I address some of your points in the faq.
- aphextron 5y ago>but note that then there’s no way of accessing updates values (in callbacks) This is by design. When you start making a component worry about what it needs to do on an update, rather than just rendering its data, you are introducing a whole new level of complexity. That's why class based React components could turn into these unwieldy confusing things where you never knew for certain what was going to render every time, because the shouldComponentUpdate method gets stuffed full of business logic.