6 ms·
One of the problems Redux is used to solve (as a global store) is peace of mind that you won't need to refactor large swathes of component trees to reparent sta
by ctidd 6y ago
One of the problems Redux is used to solve (as a global store) is peace of mind that you won't need to refactor large swathes of component trees to reparent state and callbacks that need to be shared or persisted across different subtrees as new business requirements arise. Hooks (especially custom hooks, which are just a composition of other hooks packaged together as a single function) make the reparenting/hoisting/anchoring of state and callbacks trivial compared to other mechanisms, providing similar peace of mind that you're not boxing yourself into an inextensible component tree. (This is regardless of context; passing down props isn't a large pain point, and is often misguided to try to solve because it leads to importable components and hidden contracts.)
Render props and HoCs solve similar problems for composition/reuse of functionality (these are all mixins at heart), but the hoistability of hooks is really the distinguishing mark in my experience.
- ricardobeat 6y agoReplacing redux with hooks that return local state can be a road to hell, as calling the hook somewhere down the tree will duplicate the state and there is no good way to prevent developers doing that.
- nightski 6y agoIMO that's not much different than introducing a new variable in your global state to represent the same thing because you didn't realize there was already one there. If you have a team you need discipline, code reviews, and leadership. No state solution is going to solve this for you.
- diob 6y agoI always try to tell folks that frameworks won't save them from themselves.
- ricardobeat 6y agoIt is not a matter of discipline. Avoiding this gotcha requires you to manually inspect your entire parent component hierarchy looking for uses of that hook - sometimes that is a plain grep away, but it is a very much invisible error. You also have to be familiar with the inner workings of the hook itself, and any other nested hooks, to verify if they use any local state or not. If it does, and you need to share that state, well, that's a big rewrite which kind of defeats the initial argument. Maybe extra tooling could be built to avoid this, if we weren't already drowning in linter plugins...
- nightski 6y agoAgain this isn't a problem with hooks. You could easily connect a redux store and reducer to some component low in the hierarchy with it's own state. Or use a class based component with some state. A developer can introduce random state anywhere and it has nothing to do with hooks themselves. If you can't trust your team to make the right decisions about where to place state, then you need to provide more guidance. I also disagree about the big re-write. Converting a hook's data/state to come from a prop instead is a very simple change.
- schwartzworld 6y agoI love how every criticism of hooks is like "if you don't take care to use them well, you get bad code".
- deleted 6y ago[deleted]
- ctidd 6y agoIt sounds like you're talking about network resolved state. I like to share this sort of state using a subscription model, where components subscribe to a property of a state root (which anchors/owns the state and how to fetch it), resolving that property's state when mounted. Where that root is in the tree determines the lifecycle of the state (e.g. it's gone when unmounted), and multiple subscribers to the same property can mount, unmount, etc. at any point during that lifecycle without duplication. I believe this model has similarities to both Apollo and Angular services, though I don't have direct experience with either.
- ricardobeat 6y agoNo, plain useState. What you're describing can be done via Context, which gets you back to "need to refactor large swathes of component trees to reparent state and callbacks that need to be shared or persisted across different subtrees as new business requirements arise", but worse. EDIT: quick example here https://codesandbox.io/s/unruffled-easley-ry6x2?file=/src/App.js:0-564 https://codesandbox.io/s/unruffled-easley-ry6x2?file=/src/Ap... This is a fairly innocuous example since the bug is immediately apparent from the button not working. Now imagine the failure mode is more subtle, and these components are about two dozen layers apart down the tree.
- ctidd 6y agoThat's not a bug with hooks, but almost a complete misunderstanding of how hooks work (*on the part of anyone who writes that code thinking it will behave otherwise). Hooks are effectively instantiated on a specific component instance. Calling useState multiple times like this is multiple instances of useState, whether or not it's wrapped in another function. And I'd like to be very clear that I strongly advise against cutting through layers with context because I wholeheartedly agree with your assessment there. You can plumb down those handles explicitly through props and enforce they're provided in a type system.
- ricardobeat 6y agoOf course it's not a bug, that is not in question. As I mentioned above, the problem is that even if you are fully aware of this, you still have to check that whatever `useSomeStatefulThing` hook you used to replace a global store does not use local state. So the assumption you can simply replace a global store with localized hooks to manage state is not true, and will certainly lead to bugs. You must use Context instead (or proo drilling as you suggest).