Maybe a good place to ask this: I've been hearing a lot of "oh we don't use Redux, we use hooks" lately, as if this obviously makes sense. Am I missing something? To me this seems like "oh we don't use Redux, we use arrays". I'm gonna need quite a few more details before I can make any sense of a statement like that. Like... what? How... how does that explain what you're doing? One of these things is not like the oth…
A Critique of React Hooks Addendum
41–50 of 56 posts
Re: A Critique of React Hooks Addendum
#42Maybe a good place to ask this: I've been hearing a lot of "oh we don't use Redux, we use hooks" lately, as if this obviously makes sense. Am I missing something? To me this seems like "oh we don't use Redux, we use arrays". I'm gonna need quite a few more details before I can make any sense of a statement like that. Like... what? How... how does that explain what you're doing? One of these things is not like the oth…
The implication behind “we don’t use Redux, we use hooks” is that they’re using React’s Context API directly as a “store”. A component at the top level has some state, probably using the useReducer hook, that state is passed down using a Context Provider, and child components can access the data with useContext. You can DIY a Redux imitation with hooks pretty easily but making it as performant as Redux is harder.
Re: A Critique of React Hooks Addendum
#43Maybe a good place to ask this: I've been hearing a lot of "oh we don't use Redux, we use hooks" lately, as if this obviously makes sense. Am I missing something? To me this seems like "oh we don't use Redux, we use arrays". I'm gonna need quite a few more details before I can make any sense of a statement like that. Like... what? How... how does that explain what you're doing? One of these things is not like the oth…
Re: A Critique of React Hooks Addendum
#44Earlier quoted context omitted.
The implication behind “we don’t use Redux, we use hooks” is that they’re using React’s Context API directly as a “store”. A component at the top level has some state, probably using the useReducer hook, that state is passed down using a Context Provider, and child components can access the data with useContext. You can DIY a Redux imitation with hooks pretty easily but making it as performant as Redux is harder.
Indeed. IIRC redux itself tried that in an earlier version but reverted to a custom update / forced render mechanism due to performance bottlenecks with Context.
- https://github.com/reduxjs/react-redux/issues/1177
- https://blog.isquaredsoftware.com/2018/11/react-redux-histor...
Re: A Critique of React Hooks Addendum
#45Earlier quoted context omitted.
It 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 pro…
No, 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/Ap... This is a fairly innocuous example since the b…
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.
Re: A Critique of React Hooks Addendum
#46Earlier quoted context omitted.
No, 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/Ap... This is a fairly innocuous example since the b…
That'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 again…
Re: A Critique of React Hooks Addendum
#47Maybe a good place to ask this: I've been hearing a lot of "oh we don't use Redux, we use hooks" lately, as if this obviously makes sense. Am I missing something? To me this seems like "oh we don't use Redux, we use arrays". I'm gonna need quite a few more details before I can make any sense of a statement like that. Like... what? How... how does that explain what you're doing? One of these things is not like the oth…
This might be of benefit: https://blog.logrocket.com/use-hooks-and-context-not-react-a... If Redux is a a vanilla-ish JS store and a Reactified access pattern, keeping the store but replacing the access pattern with React Contexts. I am not a React expert.
Re: A Critique of React Hooks Addendum
#48Earlier quoted context omitted.
IMO 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.
It 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…
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.
Re: A Critique of React Hooks Addendum
#49Earlier quoted context omitted.
Are people just using "hooks" to mean "all new functionality in React that happens to be available to/as hooks"? To me it's like (actually very like) "oh we're doing that with methods". Uh. OK? Cool I guess? That's not informative.
Maybe. I really don't know. There are some places where functionality is dependent on hooks. React fast refresh won't work with class components. (useState is the hook alternative to class style state management and fast refresh is fancy hot reloading but with persisted state and only reloading modified modules/components). There are few escape hatches only built as hooks mostly for improving performance issues which…
Re: A Critique of React Hooks Addendum
#50Earlier quoted context omitted.
That'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 again…
Of 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 ins…
At the same time, unless I'm really misunderstanding the example you shared, I can't see the problem or mental overhead you're mentioning where we need to be vigilant about having the same hook used in multiple places or understanding the scope of the hook's "local" state and callbacks. That's the point of hooks as a unit of encapsulation -- just like with a class, each use of a hook is a different instance, and the scope is that of the instance (and hook instances are scoped/bound to the lifecycle of the containing component instance). The code in the problem you showed is equivalent to calling setState on the one component instance and expecting that to show up on another instance which happens to be of the same component class. If we understand how React component instances work, that's clearly not the case (state isn't broadcast across instances), and the same goes for hooks.
Looking at what I mean by the improved portabiliy/hoistability of hooks I mentioned in previous comments, let's say we have three components: App, Foo, and Bar. App renders Foo and Bar as children.
We have a business requirement that Foo has some behavior which contains multiple pieces of state and bindings to multiple React lifecycle events (e.g. mount). What we don't know is whether we'll ever need to share that behavior with Bar.
React class API: We need to write bindings for this behavior against class state and lifecycle methods, intermingled with other code. This intermingling means you know there's a good deal of refactoring to do to move the behavior upward to App if we ever need to share the behavior with Bar. As a component grows, more and more logic intermingles on those lifecycle methods and makes refactoring more challenging.
class Foo extends React.Component {
state: {
behaviorState: {...},
unrelatedState: {...},
};
componentDidMount() {
initBehavior(this.props.input);
initUnrelated();
}
...
}
React hook API: We can easily write this behavior in a custom hook, encapsulating the behavior in some combination of useState, useEffect, and other React hooks. Now we have a self-contained useBehavior hook, by definition isolated from any local state, which you can choose to use within Foo. const Foo = ({ input }) => {
const behaviorState = useBehavior(input);
const unrelatedState = useUnrelated();
return ...;
};
At this point, we're not so worried if we need to hoist that one function call up to App and pass the hook's returned handle (behaviorState) back down to Foo and Bar at some point in the future.If this example sounds trivial, I've packaged up 500+ lines of functionality in a single self-contained hook before, and that's all exposed as a single function that consumers can treat as a full encapsulation of all of that functionality, while not worrying if they need to hoist that function call up and pass the output down at some point in the future.
I'm not talking about calling the same hook in Foo and Bar (these are different instances), nor sharing local state sideways across different invocations of hooks, but rather having full confidence it won't be hard to re-anchor that hook upward in the component tree if need be.