4 ms·
The extendState one confused me a bit as I just keep one “atom” of state per useState since Hooks were introduced (so you’d end up with foo, setFoo, bar, setBar
by tomduncalf 5y ago
The extendState one confused me a bit as I just keep one “atom” of state per useState since Hooks were introduced (so you’d end up with foo, setFoo, bar, setBar). I hadn’t really considered doing it the way they are doing it (which feels more like the old this.setState API). How do other people use it?
- johnday 5y agoI agree. Though it's possible that it makes sense to have some ideas "unified" into a single state variable if only some configurations of the pair of values are sensible, and you want to keep both in lockstep - or if things trigger off of state changes in any of several values, and you want to modify several values at a time.
- hn_throwaway_99 5y agoThere are other comments here making the same general point, that extendState seems like a bit of a weird, and unnecessary, use case, and at first reading the blog post I agreed. However, it "clicked" with me when I saw how they use their usePromise hook (to me it seems like their useAsyncExtendedState hook is only really useful in conjunction with their usePromise hook). That is, it is extremely common in React to call some remote API, then update the state when the Promise resolves. You also have a common set of things you want to handle: showing that the request is still pending, handling an error, updating a part of the state with some subset of the response object when the Promise resolves, etc. It's very possible to do this in React without these custom hooks, but you'll usually find you have top-level state variables in your component that mirror the success/error/pending states. You need to do that all over the place. Using these custom hooks makes it easy and consistent to make these remote calls but handling the stuff about the remote Promise request is essentially "contained" by the result of usePromise. Seems like a really nice, clean way to get consistency across remote calls in all your components.
- roastnewt 5y agoI use hooks the way you do for independent information, but when the states are related (like a userID and a userName for example), then it's better to have them as properties in the same object. If they tend to both change at the same time, that could cause a double re-render, as well as temporarily having an inconsistent state when one has updated but the other hasn't yet.
- loh 5y agoYeah. It depends on the data structure and how you'll use/update the state. `extendState` exists for when the situation calls for it. I avoid abusing it.
- fastball 5y agoI don't really understand extendState because it seems like somewhat pointless syntactic sugar for: setState((previousState) => ({...previousState, foo: false}))
- ui4jd73bdj 5y agoI would imagine it would help more with nested objects. ({ contact: { address: "" } }) as opposed to ({ ...prev, contact: { ...prev.contact, address: "" } })
- zeroonetwothree 5y agoI guess how you should handle that is ambiguous. Probably best to be explicit for that reason.
- loh 5y agoFor deeply nested data, I would probably use `immutability-helper`. Many deep spread operators can get ugly and confusing pretty quickly, in my experience.
- loh 5y agoYep! It is syntactic sugar. I personally like it though because it results in slightly cleaner, more concise code. It's just easier on my eyes.
- fastball 5y agoNormally syntactic sugar is fine and has a near-zero cost, but in this case I don't think that's actually true. Given the way hooks are structured, if I want to use both `setState` and `extendState` in a `useCallback`, I'd need to pass both to the dependency array, which is just extra effort (which defeats the purpose of the syntactic sugar imo).
- what_is_orcas 5y agoI think I use it like you do. From my perspective, it's just much clearer when I haven't seen the code in a while and need to immediately understand what's going on. Also, I'd be interested in profiling the two different ways of managing state because I can't imagine that recreating a composite state object could be as fast as updating one value, but I haven't done that and I haven't really looked into how the spread operator works under the hood... maybe it's super fast.
- neolefty 5y agoFor medium-sized state — for example a single-level object — I usually use useReducer, with the "reduce" function a simple destructure merge. The reducer looks like this: const shallowMergeReducer = <T extends {}>(state: T, action: Partial<T>): T => Object.freeze({ ...state, ...action, }) It ends up looking a lot like the "setExtendedState" function in the article. Often I'll throw in a few helper functions in a useMemo, too, and then provide the whole thing through Context. It's like a mini-Redux for some section of the app.