[HN Gopher] Two custom React hooks
       ___________________________________________________________________
        
       Two custom React hooks
        
       Author : loh
       Score  : 174 points
       Date   : 2021-12-17 13:30 UTC (9 hours ago)
        
 (HTM) web link (blog.molecule.dev)
 (TXT) w3m dump (blog.molecule.dev)
        
       | mattwad wrote:
       | Surprised at the negative comments already. I'm not sure half of
       | the commenters use React on a daily basis. I've re-written these
       | same hooks in various ways, as well as used ones written by
       | others. This look great to me, personally! I've been using a
       | weird implementation of useReducer() (most commonly suggested on
       | SO) but extendState() is a more elegant solution.
       | 
       | Couple suggestions:
       | 
       | * the repetition of "read" `const [ readRequest, requestRead ] =
       | usePromise(read)` makes it hard for me to keep these things
       | separate. It would be easier if you had a real life example, like
       | "get users" or something but even somethin like `const [
       | apiState, fetchApiState ] = usePromise( apiRequest )` would be
       | better IMO.
       | 
       | * `readRequest.cancel` - sometimes I've wanted to cancel a
       | request but it's not an error to show the user, but it looks like
       | the view wouldn't be able to tell the difference from a regular
       | http error
       | 
       | * `readRequest.reset(`error`)` could be another way to just reset
       | a single property, instead of having to use brackets
       | 
       | * uh where is the code for the hooks? A direct link to code or
       | even a library would be great
        
         | loh wrote:
         | That's a good call. I'll change those to better names.
         | 
         | Also, you can call `cancel()` (with no arguments) if you don't
         | want an error.
         | 
         | You can find the code for the hooks here:
         | https://github.com/Molecule-dev/molecule-app/tree/_e745872f9...
         | 
         | I'll add a direct link to that too. I really appreciate the
         | feedback.
        
       | brundolf wrote:
       | I think everyone has their own implementation of usePromise; it's
       | weird to me that it isn't included out of the box
        
       | notpachet wrote:
       | > In the case of modern front end engineering and React
       | especially, you can reduce everything down to two simple
       | concepts... > Rendering the current state > Updating the state
       | 
       | Another day, another React team coming to the belated realization
       | that hooks are an inelegant solution to problems that have
       | already long been solved. Separate your view layer from your
       | application state. Get all those network calls the hell out of
       | there. Relegate your components to simple stream transformers --
       | props in, HTML out. If you're doing anything other than that,
       | your function components aren't really pure functions.
       | 
       | It's frustrating to watch an entire swath of the industry
       | continually rediscover its own inadequacies year after year...
        
         | vpfaulkner wrote:
         | I agree that "separating concerns" is generally a good thing.
         | 
         | However, the issue is that the traditional division of concerns
         | is more difficult to maintain in today's web apps. Compared
         | with web pages 20 years ago, web apps today are dense,
         | interactive and complex. You might have dozens of UI components
         | in a single page, each with their own piece of state, business
         | logic and styling. Moreover, state, business logic, and
         | presentation are oftentimes tightly coupled by design: eg,
         | dragging this slider changes its shading using a complex
         | algorithm.
         | 
         | Therefore, it's becoming more advantageous to decouple
         | individual _ui components_ , each with their own
         | state/logic/styling, than it is to, say, stick all of the state
         | your web app deals with in a single place.
         | 
         | In other words, it makes sense to encapsulate all that code
         | related to that crazy slider in one place, even if that
         | includes state, styling, algorithm, etc...
        
         | z3t4 wrote:
         | I heard that React is only the "view" but when actually trying
         | to make an non trivial app (lots of async calls) logic and
         | everything gets entangled. Please show me an app where react is
         | only a view withot side effects
        
         | codecurve wrote:
         | This isn't a solved problem.
         | 
         | The React ecosystem has spent the last few years trying a model
         | where the application layer separated from the view layer with
         | a pure functional state management solution called Redux. The
         | overwhelming response? People didn't like it.
         | 
         | Decoupling systems is a trade-off. Pull your network requests
         | out of your components and you have two bits of code that are
         | easier to test. Indirectly, the component is still going to
         | call that code, and it's up to you to manage the complexity of
         | that indirection.
         | 
         | Not every application needs separation of concerns, and in
         | many, colocation of concerns reduces the cognitive burden,
         | because you can reason about components in isolation. To me,
         | that's a more powerful guarantee than a function being strictly
         | pure.
        
           | notpachet wrote:
           | I agree with your point that not every application needs this
           | degree of separation of concerns. But the problem, in my
           | experience, is that individual developers are not very good
           | at knowing where that line is. And the line can move over
           | time as the application grows.
           | 
           | For any project that I'm responsible for, I don't feel
           | comfortable designing around a paradigm unless there are some
           | guard rails to prevent developers on my team from
           | accidentally tying the code in knots. How enjoyable is this
           | for the developers? Are they sprouting angel wings and
           | playing the lyre as they write code in iambic pentameter?
           | Probably not, no. They have less freedom of motion than if
           | they were left to their own devices.
           | 
           | This is a larger debate: where to fall on the spectrum
           | between unadulterated developer bliss and having an
           | application that is still maintainable in 5 years. I don't
           | put much stock in developer bliss, but I do appreciate that
           | the sword cuts both ways.
        
             | catlifeonmars wrote:
             | I'm curious if the statement "happy developers create
             | better systems" holds any weight.
        
           | acemarke wrote:
           | Hi, I'm a Redux maintainer. I've written extensively about
           | the fact that A) Redux _has_ been overused, B) that many of
           | the complaints were really more about the standard code
           | patterns needed and the "boilerplate" involved, and that C)
           | "modern Redux" with our official Redux Toolkit package and
           | the React-Redux hooks API has solved those "boilerplate"
           | concerns.
           | 
           | Redux is still by far the most widely used state management
           | tool with React apps (my estimates are around 45-50% of React
           | apps use Redux), and we try to give clear guidance in our
           | docs on when it does and doesn't make sense to use Redux.
           | 
           | FWIW, we get _highly_ positive feedback on a daily basis from
           | users who tell us how much they love using Redux Toolkit.
           | 
           | Resources:
           | 
           | - https://blog.isquaredsoftware.com/2018/03/redux-not-dead-
           | yet...
           | 
           | - https://blog.isquaredsoftware.com/2021/01/context-redux-
           | diff...
           | 
           | - https://blog.isquaredsoftware.com/2021/05/state-of-redux-
           | may...
           | 
           | - https://blog.isquaredsoftware.com/2021/05/learn-modern-
           | redux...
           | 
           | - https://blog.isquaredsoftware.com/2017/05/idiomatic-redux-
           | ta...
           | 
           | - https://redux.js.org/tutorials/index
           | 
           | - https://redux.js.org/tutorials/essentials/part-2-app-
           | structu...
        
             | robertcorey wrote:
             | good to see mark fighting the redux fake news in the
             | comments as always.
        
             | monocosm wrote:
             | How did you get to your estimation of 45-50%? That sounds
             | waaaaay too large.
        
               | larsnystrom wrote:
               | According to npmjs.com react has 13.6M downloads/week and
               | redux has 6.7M. So 45-50% sounds about right.
        
               | sroussey wrote:
               | Tip: If you want to increase your numbers relative to
               | another package, ship more versions over the same time
               | period.
        
               | [deleted]
        
               | ratww wrote:
               | That figure is "Weekly Downloads", not in total. None of
               | them had updates in less than two months. Also, React has
               | published 10x more versions than Redux. So if anything,
               | Redux might be more popular than GP assumed.
        
             | Lhiw wrote:
             | Has redux fixed its broken implementation of event sourcing
             | and cqrs yet or is it still encouraging people to execute
             | side effects in reducers?
        
             | trulyme wrote:
             | Kudos on Redux Toolkit! Anyone who complains about
             | boilerplate with Redux has obviously not tried the Toolkit
             | version. Very impressive.
             | 
             | A bit sad that immer was used (a bit too much magic for my
             | taste), but I can understand the reasoning, and if you just
             | accept it, it's ok.
             | 
             | Thank you and all the other maintainers for your work!!!
        
             | pcthrowaway wrote:
             | Acemarke isn't just "a Redux maintainer", he's incredibly
             | active on Reddit, HN, and Discord, helping people
             | understand Redux better, and also, when it doesn't make
             | sense to use it.
             | 
             | I can't speak to his code contributions, but in terms of
             | documentation, tutorials, and community engagement, most
             | open source projects would be lucky to have similarly
             | prolific contributors.
        
             | what_is_orcas wrote:
             | I'll add to that: I love redux and most of the _complaints_
             | or _concerns_ that I 've heard about using redux (from a
             | few teams) have been misunderstandings of how to integrate
             | redux into an existing application or an application
             | design. I think this has mostly come from junior-level
             | folks (independent of title, there are a lot of non-junior
             | devs who can't integrate new patterns into their "senior"
             | level understanding of code, but I digress) and folks who
             | don't work on the front end much (or ever). I was one of
             | those folks until I started a side-project that used redux
             | and I had to implement it in a greenfield project and had
             | the liberty to refactor as I progressed and my mental-model
             | "updated" to integrate the new framework.
        
               | vlunkr wrote:
               | My complaints with redux were that people couldn't leave
               | it alone. At least in my experience, there was a lot of
               | middleware or libraries built on top of what was a
               | conceptually very simple library.
               | 
               | Using the redux-toolkit and the hooks, with none of the
               | additional junk is a great experience.
        
               | eitland wrote:
               | Even better is my experience in the project were I work
               | now and were for the first time we are working to remove
               | redux.
               | 
               | I have respect for what acemarke writes above and I think
               | acemarke is right that redux has been overused a whole
               | lot.
        
             | [deleted]
        
             | toinbis wrote:
             | Thanks for great comment! I have one question.
             | 
             | The biggest thing I miss in react ecosystem is decent redux
             | ORM. https://vuex-orm.org is just so great for so many use
             | cases (agree that it might an antipattern in many
             | situations). Is there any chance that
             | https://github.com/redux-orm/redux-orm, which was actually
             | what inspired vuex-orm, would get more love from anyone to
             | become an actively maintained library?
             | 
             | Thanks
        
           | swyx wrote:
           | idk if "the overwhelming response" is people dont like Redux.
           | some people are very vocal about their dislike, yes. But 1/3
           | of survey respondents use Redux despite other choices
           | existing. theres a reason it won the Flux wars.
        
             | codecurve wrote:
             | Maybe overwhelming was too strong, but I don't see many
             | people getting excited about building new projects with
             | Redux any more. Presumably some proportion of that 1/3 are
             | stuck on Redux, unwillingly?
             | 
             | I like (and use) Redux on a daily basis, and I don't think
             | it's a sensible choice for lots of React apps. Maybe the
             | overwhelming rejection that I've witnessed is people
             | discovering that they used it for stuff they shouldn't
             | have.
        
             | azangru wrote:
             | > theres a reason it won the Flux wars.
             | 
             | It won the Flux wars, but there are state management
             | approaches based on paradigms other than flux. There's at
             | least the model that Recoil/Jotai uses (atoms?), the model
             | that MobX/Valtio uses (mutable observable state), the model
             | based on state machines (XState), the model heavily using
             | React context (Constate), etc.
        
           | zaksingh wrote:
           | A big challenge with the React ecosystem is that it's the
           | first technology many new developers work with. They don't
           | have a frame of reference for what alternative approaches
           | exist, and therefore 'best practices' are taken on faith and
           | followed blindly until their nuances can be learned through
           | experience.
           | 
           | That's not a bad thing (and it's better than the alternative
           | of not caring for design patterns whatsoever). It's part of
           | the learning process, and since there are so many beginner
           | developers who are using React, their perspective is much
           | 'louder' than in other dev ecosystems.
           | 
           | You can see this in the absurd amount of introductory-level
           | React content posted to Medium, DevTo, Twitter, etc. This has
           | bred a very strong 'follow-the-leader' culture where, when
           | the one person is the room who _does_ know what they're
           | talking about makes a statement, others will repeat it
           | verbatim without understanding its nuances due to a lack of
           | experience/context.
           | 
           | Redux suffered heavily from this. New React devs in 2017 were
           | faced with mountains of tutorials which all used Redux. Many
           | of these tutorials were written by other newbies. Your mental
           | model of React dev was then shaped around Redux. Therefore
           | you would put everything you could into the Redux store,
           | which is a bad idea -- you usually don't need form state in
           | there, for example.
           | 
           | Then some React thought-leaders saw this problem and
           | inadvertently created a counter-movement by raising how Redux
           | was overkill for some use cases, which was misconstrued as
           | 'you shouldn't use redux _at all_'. The pendulum has been
           | swinging back and forth ever since.
           | 
           | Yes, not every application needs separation of concerns. But
           | some definitely do. It's not black and white, and that
           | unfortunately means there's no definable 'best practice' that
           | can be tweeted or blogged about and followed blindly -- it
           | just has to be learned from experience.
        
             | codecurve wrote:
             | I would go one step further and say that the counter-
             | movement is the visible effect of newcomers discovering
             | that separation of concerns was a bad decision for the
             | simple apps they were building.
             | 
             | Or maybe more accurately, discovering that it's often
             | simpler to separate by concern at the component level, than
             | at the app level.
             | 
             | We all ride the pendulum until we find the shade of grey
             | which works best for us.
        
             | anchpop wrote:
             | > Therefore you would put everything you could into the
             | Redux store, which is a bad idea -- you usually don't need
             | form state in there, for example.
             | 
             | Why's that? IME it is usually simplest to just put
             | everything in redux. For example, if you have a form under
             | some kind of tab navigation thing, you'd ideally want the
             | form state to be preserved even when they tab out and then
             | back in. Putting it in redux means you don't really have to
             | think about it
        
           | notpachet wrote:
           | Sorry, but I have stopped using "developers don't like this"
           | as a measure of the technical quality of anything. A lot of
           | developers like things that are pleasurable to them in the
           | small, but harmful to the codebase in the aggregate,
           | especially over years of maintenance cycles.
        
             | arvinsim wrote:
             | Doesn't matter. Every developer will still be at the mercy
             | of the whims of the majority unless you find your own
             | niche.
        
               | dgb23 wrote:
               | Lowest common denominator.
        
               | notpachet wrote:
               | I'm writing this stuff to try and push back against the
               | whims of that majority, because I think they're ill-
               | founded. Just because a majority of people believe
               | something to be good and true, doesn't automatically make
               | it so. It still has to stand up to empirical evaluation
               | on its own merits.
        
             | whakim wrote:
             | I'm not sure I agree with this. One of the elements of a
             | well-designed system (whether we're talking about software
             | libraries or anything else) is that the designer should
             | reward people for doing what they like. If you design
             | something where the correct approach cuts against people's
             | natural inclinations, they'll just use something else. The
             | correct answer is to design paradigms that make the right
             | thing pleasurable.
        
               | notpachet wrote:
               | I agree that systems should be rewarding to the user. But
               | there are rewards and there are rewards. There are short-
               | term dopamine fix rewards, and then there are the rewards
               | that you can only really appreciate after having invested
               | some time and energy first. It's like fast food vs a
               | lifetime of diet and exercise.
               | 
               | The churn in the frontend ecosystem reminds me a lot of
               | fad dieting: people have never really experienced working
               | in a paradigm that enables them to stay healthy through
               | regular diet and exercise over the long term, so they
               | turn to the latest shiny gizmo hoping that this time it
               | will be different.
        
               | whakim wrote:
               | I agree with you that there's a balance to be had here
               | (although I will note that there's a pretty big
               | difference in physiological effect between tens of
               | thousands of years of evolutionary history and a few
               | years of writing code). That being said, I don't think we
               | should make "rewarding to the user" a second-class
               | citizen to "technical quality" - I think it's _an
               | important component of_ good technical quality that
               | something is pleasurable to use. My impression is that a
               | lot of self-serious systems designers don 't embrace this
               | view because it's easier to blame the technical
               | incompetence of their users.
        
             | onion2k wrote:
             | This is true, and _especially_ true for tutorial and course
             | authors. There are a lot of low quality tutorials that are
             | essentially a dev talking about how they prefer to wrute
             | code rather than teaching any useful, generic material that
             | applies universally. Often what works in their courses for
             | a lone junior dev would not scale to a small team, and
             | following their ideas would produce a pretty terrible app.
        
           | toinbis wrote:
           | Am wondering what react community thinks of DDD.
           | 
           | I've been reading "blue" DDD book (by Eric Evans) and "red"
           | book (by Vaugh Vernon) and that was a completely "my whole
           | life was a lie" type of experience and relief at the same
           | time. It's just so great to have the principles of who to
           | structure the code. It, by definition makes, your codebase
           | structure meaningful. Because it's structured according to
           | some common knowledge, not your random thoughts at the time
           | you were writing code.
           | 
           | I was surprised to find so little DDD react sample codebases.
           | Let's say for backend there is huge amount of samples, i.e.
           | https://github.com/kgrzybek/modular-monolith-with-ddd . For
           | react/frontend I have bookmarked only
           | https://github.com/talyssonoc/react-redux-
           | ddd/tree/master/sr... and few more, but those others does not
           | meet the optional criteria i like really much - at the
           | highest (or at app) level all codebase need to have folders
           | app, domain, infra and ui. Simple rule, but simplifies life a
           | lot.
           | 
           | So my question is - is DDD for some reasons not very
           | applicable for app frontend development. Or it just never
           | became popular. Or maybe DDD is popular amongst react
           | developers, just I am not aware of this.
           | 
           | Many thanks for any ideas and comments!
        
         | nfRfqX5n wrote:
         | your preference for model/view/purely presentational components
         | would benefit from using hooks in your model layer as opposed
         | to a class component with the old lifecycle hooks. hooks are
         | the model
        
           | azangru wrote:
           | > hooks are the model
           | 
           | Unless you mock a hook at the module import level (which is
           | hacky), you lose the easy testability of your component.
           | That's long been bothering me about hooks, although I use
           | them plenty.
        
         | ajkjk wrote:
         | I love hooks. Just saying. They're so much better than what
         | came before.
        
           | worldsayshi wrote:
           | I also really like them but I fear that they are a bit
           | magical, i.e. they solve a problem in a way that feels very
           | simple but might end up blowing up in complexity once you
           | look at them from a certain angle. Like when debugging
           | certain state issues. They are very nice until they break in
           | unexpected ways.
        
         | dstroot wrote:
         | > In the case of modern front end engineering and React
         | especially, you can reduce everything down to two simple
         | concepts... > Rendering the current state > Updating the state.
         | 
         | Not a fan of the negativity in this post (I use hooks all the
         | time) but the advice to separate concerns about managing state
         | and rendering the state is GREAT for ANY architecture.
        
         | loh wrote:
         | > Separate your view layer from your application state.
         | 
         | It is actually separated, not from React though because React
         | will need the state (data) at some point anyway. We'll go into
         | more detail on that in the next post. To touch on it briefly
         | here, shared application state exists on its own at the top
         | level of the app, as a composition of stateful hooks. See here:
         | https://github.com/Molecule-dev/molecule-app/tree/6e2456e216...
         | 
         | Every possible API request also exists on its own outside of
         | anything React (view). See here: https://github.com/Molecule-
         | dev/molecule-app/tree/6e2456e216...
         | 
         | Or for a more specific example, see this API resource route
         | index: https://github.com/Molecule-dev/molecule-
         | app/blob/6e2456e216...
         | 
         | I may be misunderstanding your complaints though. I appreciate
         | your feedback.
        
           | notpachet wrote:
           | What I mean is that you're violating standard separation of
           | concerns when you have components that themselves are capable
           | of dispatching network calls and updating the state in an
           | async way _directly within the components_. As a result,
           | those components no longer have an instantaneous view of the
           | universe, and that makes them harder to test, harder to
           | reason about as isolated units of abstraction, and so on.
           | 
           | > Every possible API request also exists on its own outside
           | of anything React
           | 
           | That's good, but I would take it once step further and
           | disallow any React component from directly invoking those
           | methods.
           | 
           | Of course you're always going to need to do something
           | asynchronously when the user clicks a button or what have
           | you. But in my opinion, it's a lot more maintainable to have
           | that just be an event that the component fires, and then have
           | something else listening for that event out-of-band (and then
           | sending the network request / updating the state to say
           | "connecting" or "request failed" and so on). What happens in
           | async land is not really a pure component's business.
           | 
           | (I know I'm definitely out of lock step with the current
           | React ethos on this, so permit a crusty neckbeard his pet
           | gripes.)
        
             | loh wrote:
             | I understand. If you get a chance, please clone the core
             | API and app (https://github.com/Molecule-dev) and play
             | around with it. Maybe you'll change your mind?
        
               | notpachet wrote:
               | That's a fair request. I'll tinker with it the next time
               | I need to spin up a weekend project.
        
         | joshfee wrote:
         | At the end of the day, components will always need a way to
         | interface with state. Often times you want local component
         | state because the cost of abstracting that tiny bit of state
         | into some top level application state is disproportionately
         | high - it just isn't a pragmatic choice. And for application
         | state, ultimately if you have any interactivity in your
         | application (which, if you have state, presumably you do), at
         | _some point_ you're going to have to do something other than
         | "map props to HTML".
         | 
         | > If you're doing anything other than that, your function
         | components aren't really pure functions.
         | 
         | Nobody claims they are pure functions. They aren't - but that
         | doesn't really matter. Its an API choice that encourages
         | productivity while still having tools for the abstraction that
         | you are advocating for.
         | 
         | State hooks can be seen as a sibling of props - both are inputs
         | to the component, but whereas props "push" data into the
         | component, hooks "pull" data from somewhere else. This _is_
         | separating concerns - when a component needs data that isn't
         | inherently something the calling parent owns, then making it a
         | prop would just be forcing that component's concerns up the
         | tree. Often it is better for it to just be an impl detail.
         | 
         | I don't find the article here super compelling, and as a
         | general practice it is often better to author your own custom
         | hooks that encapsulate things like the underlying HTTP request
         | away so that your component is only dealing with its own
         | concerns, but functionally there's no difference and its just a
         | matter of how you split up your code.
        
           | octet1 wrote:
           | > the cost of abstracting that tiny bit of state into some
           | top level application state is disproportionately high - it
           | just isn't a pragmatic choice
           | 
           | What other costs are there besides boilerplate?
        
             | joshfee wrote:
             | In my experience, the cost of writing and also maintaining
             | code is proportional to not necessarily the (typically
             | fixed) cost of the boilerplate, but the "distance" between
             | the various pieces needed for things to work.
             | 
             | Let's say I need a single bit of state in my component. If
             | I store that in a local `useState(false)` and update it
             | with `setState(newValue)`, it is extremely easy to write,
             | and also follow what is happening and how it is happening.
             | 
             | But if dogmatically say that this is poor encapsulation -
             | this state should live in some Redux store, with actions
             | and action creators, reducer functions, etc. there is
             | inherent complexity, and I think in the case of a single
             | bit used in a single place that it is pretty easy to see
             | that the complexity is disproportionate to the actual needs
             | at hand.
             | 
             | But the way I see it is a sliding scale - your state starts
             | to become more complex? or maybe how the state is updated
             | requires some additional business logic? Maybe you want to
             | start using that state in different places? Any of these
             | reasons can be cause to introduce abstraction, but my
             | preference is to introduce the smallest abstraction that
             | achieves the benefit you're after.
             | 
             | Sometimes this abstraction is hoisting state up - now your
             | component doesn't manage its own state, but rather it
             | communicates with its parent via props+callbacks. Now its a
             | little harder to follow (need to consider this component,
             | its parent, and how the prop/callback are being used), but
             | for that complexity we now get to share that state with
             | sibling components.
             | 
             | Sometimes this abstraction is moving it to a reusable hook.
             | Now you have a similar scenario where to understand how
             | things are working you have to understand the component and
             | the hook, but for that cost we can now share business
             | logic.
             | 
             | Sometimes that abstraction is moving it into a redux store,
             | with all the boilerplate that goes with it. For the right
             | use case this is not a bad thing, but I would only want to
             | do this if I'm getting a proportionate amount of value for
             | the increased cost.
        
         | faizshah wrote:
         | I have been coding in react full time for the last 8 months in
         | large codebases. I have to say that initially hooks were hard
         | to understand especially useEffect but then as I got
         | comfortable with them they were great to use and I was really
         | productive. But then, as I started shipping production code
         | with react I found that hooks are extremely difficult to debug
         | and very complex to wrap your head around when debugging
         | because of side effects and implicit behavior.
         | 
         | I now really dislike hooks because of the amount of implicit
         | behavior and useEffect poisons the codebase to add even more
         | implicit behaviors/side effects to your application.
         | 
         | They make the rendering behavior really difficult to reason
         | about. I caught a bug where you had to click a button twice and
         | the reason why it happens is extremely difficult to reason
         | about: https://stackoverflow.com/questions/58106099/react-
         | onclick-n...
         | 
         | In my opinion what we need is an explicit state machine of the
         | rendering of a component like vue's component lifecycle. And
         | these hooks we are using for state management we should instead
         | use xstate to make the state transitions of a component
         | explicit. React hooks (and redux and react-query) are bad (in
         | my opinion) because they are easy to write with and hard to
         | reason about.
        
           | wldcordeiro wrote:
           | I think that the React devtools haven't kept up with how
           | hooks work. They let you look at your props and state as they
           | are now but not look at the "timeline" like something like
           | the Redux devtools do. My struggle debugging is always trying
           | to look at intermediate states and only having the current
           | app state available, pausing the debugger didn't work as the
           | React devtools crash when you do that.
        
           | imbnwa wrote:
           | Can you expand on Redux?
           | 
           | My gripes w/ Redux is that its pattern is clearly better
           | suited to a runtime with more to offer functional programming
           | than first-class functions, hence all the boilerplate and
           | effort to abstract all that boilerplate, as well as deciding
           | how to pick how you'll get to granular updates (which is
           | where, from where I'm standing, React Hooks' design starts).
           | 
           | But in comparison to React Hooks, its vastly superior for
           | affording clear separation of boundaries in an app.
        
             | faizshah wrote:
             | So maybe I'm not the best person to speak specifically on
             | redux as we use redux sagas so I can't give a fair take on
             | redux itself. However my gripe with redux sagas and react
             | query is pretty simple, react query is implicitly defined
             | implicit behavior, redux sagas is explicitly defined
             | implicit behavior and xstate is explicitly defined explicit
             | behavior.
             | 
             | What do I mean by that? The applications that I work on do
             | a lot of composition of apis, reshaping the data and
             | caching on the frontend since our backend teams are micro
             | services teams. What happens is that our codebase needs to
             | define multi api workflows and data transformations which
             | we define using redux sagas or react query (we either adopt
             | one or the other for the codebase).
             | 
             | The problem with the react query way of doing this is that
             | the query has its own built in lifecycle it goes through
             | stale -> fetching -> loading -> success/fail this is
             | implicit behavior that you don't have visibility into why
             | it is making a transition. It becomes very painful for
             | large workflows of api compositions to debug this kind of
             | implicit behavior.
             | 
             | Redux sagas on the other hand is explicitly defined. We
             | have these channels of communication over which messages
             | are defined (actions) which cause some explicitly defined
             | behavior to happen (sagas, reducers). Now why is this
             | problematic? In a large codebase of micro services
             | transactions we are dispatching actions in response to a
             | parent action forming an explicitly defined implicit
             | behavior.
             | 
             | So for example with react query if I am debugging an
             | unexpected state in prod for a bug I have to reason about
             | the way react query is processing a query in order to debug
             | it due to the implicit behavior. In redux sagas world I
             | have to reason about the implicit state machine of actions,
             | sagas and reducers that a workflow kicks off.
             | 
             | This is why I don't like these two styles of organizing the
             | application state. It becomes difficult to reason about the
             | behavior but the redux/redux sagas way is easier to reason
             | about because at least we explicitly defined the implicit
             | behavior.
             | 
             | Also make sure to note I am talking about a complex web app
             | on a micro-services api (frontend does a lot of api
             | composition work), I am sure these are elegant solutions in
             | the right context.
        
         | brundolf wrote:
         | I didn't like Hooks out of principle when I first read about
         | them. I still get slight heebie-jeebies when I think about the
         | fact that they rely on calling-order for identity. But having
         | now used them for six months, I can't deny the practical
         | benefits they bring.
         | 
         | I think it helps to not think of them as "just a way of letting
         | you write all your components as functions". That was always a
         | red herring; even before hooks, React "functional components"
         | were impure stateful objects at runtime. Only your part of the
         | code was ever kind-of pure. It was an illusion, and Hooks just
         | pulled back the curtain.
         | 
         | I think of Hooks as more like a DSL for declaring
         | externalities, which just happens to take the form of a series
         | of function calls. They're clunky for building complex memoized
         | value-graphs [gazes longingly at MobX], and complex useEffect
         | behaviors can get really hard to follow real quick. But for
         | building average web apps - where 90% of the time you're either
         | pulling in standard externalities or defining little pieces of
         | primitive state - they make things really ergonomic. They also
         | enable some fancy optimizations and scheduling on the React
         | side, from what I understand. Overall I think they were the
         | right move for a for-the-masses UI framework.
        
           | city41 wrote:
           | They also eliminate subtle bugs that can emerge from `this`
           | being mutable in classes, and they make the React lifecycle
           | more intuitive. Rather than thinking about whether the
           | component did mount, will mount or will unmount, you just
           | associate side effects with certain states.
           | 
           | Dan Abramov has a nice write up on the pitfalls of mutable
           | this: https://overreacted.io/how-are-function-components-
           | different...
           | 
           | With all that said, I would still say I'm not really a fan of
           | hooks. I more see them as a necessary evil than a great
           | feature. I try to use them as minimally as possible.
        
             | brundolf wrote:
             | Worth noting that mutable-this can be made to work by using
             | something like MobX. It's not a fundamental problem, just
             | one that React declined to solve directly, which I can't
             | totally blame them for.
        
           | bern4444 wrote:
           | > Hooks as more like a DSL for declaring externalities,
           | 
           | That's exactly right in my opinion. Hooks are the place to
           | declare stateful and effectful (aka impure) logic. Using a
           | hook is an equivalent signal to using a Future, or an IO in
           | other languages like Scala.
           | 
           | Often combinations of useState and useEffect are best written
           | as a custom hook to reduce noise in the containing component.
           | An easy example can be seen in something like
           | const [ name, setName, isValid ] = useValidatedState(state,
           | validationFunction);
           | 
           | Seeing that you can easily imagine how it would be
           | implemented:                 const useValidatedState =
           | <T>(initialState: T, validationFunction: (val: T) => boolean)
           | => {              const [state, setState] =
           | useState(initialState);         const [isValid, setIsValid] =
           | useState(validationFunction(initialState);
           | useEffect(() => { setIsValid(validationFunction(state)) }
           | [state]);              return [state, setState, isValid];
           | };
           | 
           | Almost always if there's a useState that has an associated
           | useEffect, it should be turned into its own custom hook.
        
         | phailhaus wrote:
         | I think you've missed the point if that's your takeaway. The
         | article's solutions actually _double down_ on hooks, proving
         | that they are powerful and expressive enough to manage complex
         | and asynchronous state precisely because they _don 't_ couple
         | view logic and application state. That's why a generalized hook
         | like `usePromise` is possible in the first place. The fact that
         | you can trivially drop in a hook to make a component start
         | dealing with asynchronous state validates the hook paradigm for
         | UI development.
         | 
         | Remember: components using hooks _are_ pure functions! That 's
         | what makes them so effective.
        
           | mikojan wrote:
           | Components using hooks are not pure functions.
           | 
           | The necessity(!) to wrap each and every basic JavaScript API
           | (useEffect v. setTimeout, useLayoutEffect v. queueMicrotask,
           | useInterval and usePromise v. using Intervals and Promises)
           | does not at all showcase the "expressive power" of React.
           | 
           | It was fun while it lasted but it's time to let go. React's
           | become JQuery
        
             | steve_adams_86 wrote:
             | How would you use setTimeout to replace useEffect?
             | 
             | useEffect works by running on each render (no dependencies)
             | or running when rendering and dependencies changed. Where
             | does a timeout factor in?
             | 
             | useLayoutEffect also doesn't work based on the microtask
             | queue - it uses React's internal scheduler, which I would
             | imagine makes sense in the context of the library.
             | Replicating it with queueMicrotask is probably not a
             | trivial task.
        
           | joshfee wrote:
           | They are _not_ pure functions. A pure function will return
           | the same value for the same arguments every time. This is not
           | true of functional components using hooks as the data
           | received from calling the hook can change over time due to
           | side effects.
        
             | imbnwa wrote:
             | A thought that occurs to me is that React Hooks are
             | Facebook engineering's attempt at an Effect monad in JS
             | that isn't well-typed
        
               | kristiandupont wrote:
               | Dan goes into the topic of algebraic effects in this
               | excellent blogpost:
               | 
               | https://overreacted.io/algebraic-effects-for-the-rest-of-
               | us/
        
               | imbnwa wrote:
               | Thank you for this, will take a look
        
       | Waterluvian wrote:
       | I like the `extendState` concept. So far, most of the time, I
       | find that modern JS is fine enough. I can absolutely see the
       | benefit of abstracting this concept. But I'm always hesitant to
       | layer on top of an API just for little improvements. The cost is
       | deceptively high: you have to re-remember your custom API and
       | others have to learn your custom API.
       | setState({...state, foo: "bar"})
        
         | andrewstuart wrote:
         | setState({...state, foo: "bar"})
         | 
         | The code you presented is wrong - it won't work the way you
         | expect. Or worse, it _might_ work the way you expect, by
         | chance.
         | 
         | If you wish to use the previous state you must do it this way:
         | 
         | setState(prevState => ({...prevState, foo: "bar"}))
        
           | Waterluvian wrote:
           | Thanks. Most importantly, though: why?
           | 
           | Is there some race condition about updates or bulk updating?
        
             | andrewstuart wrote:
             | Briefly - it is because React batches and executes setState
             | calls some time in the future - thus you MUST use a
             | function call to get a non-stale state.
             | 
             | Refer to my more detailed explanation elsewhere in this
             | thread.
        
               | Waterluvian wrote:
               | Thanks. I see the example in the docs shows this method.
               | StackOverflow shows many people sharing the same. I've
               | never had this problem so I'm probably quite lucky. But
               | it makes complete sense. I'm surprised it's not called
               | out on the docs page for setState.
        
               | andrewstuart wrote:
               | >> I've never had this problem so I'm probably quite
               | lucky.
               | 
               | Possibly, but more likely you've had weird bugs that
               | never quite made sense and eventually things seemed to
               | work so you moved on, but what was actually happening was
               | a stale state problem. It's hard to write big React apps
               | using the wrong way to udpate setState without bugs.
        
       | bayesian_horse wrote:
       | I think even those examples border on usecases for more complex
       | state management along the lines of redux, Zustand etc. To me,
       | useState with a complex object is an antipattern. I'd much rather
       | use multiple useState invocations. This eliminates the need for
       | extendState and for handling functions and promises.
       | 
       | Also, take a look at Redux Toolkit Query and React Query. Both
       | provide advanced query state management based on hooks.
        
         | maest wrote:
         | > To me, useState with a complex object is an antipattern. I'd
         | much rather use multiple useState invocations.
         | 
         | The issue with that is that sometimes you need to make
         | transactional changes to your state (i.e. either update 3
         | variables together, or not update them).
         | 
         | That gives you 2 options: 1. bundle the state up in a complex
         | single object with useState 2. extend the space state of your
         | app to allow for mid-transaction rendering (i.e. only 2 of the
         | vars have updated and the 3rd has not).
         | 
         | #2 feels a lot harder to me, tbh (although I do strongly
         | dislike #1. and would like an alternative.)
        
           | joshfee wrote:
           | This sounds like the use case for `useReducer`
        
           | Jenk wrote:
           | That scenario to me is a sign of a greater workflow than what
           | should be in the view. Some other model should be handling
           | that transactionality, not the view itself.
        
           | steve_adams_86 wrote:
           | In React 18 I believe this is handled via batching of state
           | updates. If you update various pieces of state within a
           | component before it has the chance to render, it will update
           | all changes at once and render once.
           | 
           | There are exceptions I've forgotten but generally it solves
           | this problem well.
        
             | jakelazaroff wrote:
             | To me, the core problem with using two hooks is that
             | there's no signal to other developers that the two pieces
             | of state must be updated atomically. Sequential setState
             | calls might be batched, but there's nothing preventing
             | someone from only calling one of them.
        
               | steve_adams_86 wrote:
               | I'm not sure I understand. Which two hooks would be
               | called in this case? In the original example I'm seeing
               | where there's anything atomic involved across multiple
               | hooks.
               | 
               | Are you referring to the usePromise and
               | useAsyncExtendedState? If so, it's okay to use those
               | hooks independently. They work fine without each other,
               | but they also work well together.
        
           | bayesian_horse wrote:
           | Sounds like a niche use case inbetween the scope of useState
           | and something like Redux.
        
           | tshaddox wrote:
           | Aren't multiple setState calls in the same tick likely to be
           | batched and only result in one new render with all state
           | values updated?
        
             | steve_adams_86 wrote:
             | Before React 18, it was possible to get multiple renders
             | depending on where your calls to set state occurred. Inside
             | different event loops, async calls, etc would lead to
             | multiple renders even if they all settled within a few ms
             | of each other and before a render occurred. The batching
             | algorithm wasn't aware of each asynchronous context.
             | 
             | In React 18 I believe this is mostly fixed.
        
         | wereHamster wrote:
         | I actually prefer the opposite, just one state per component.
         | And I use immer for that (use-immer, specifically). Most of my
         | components have one set of props (passed down from the parent),
         | one set of state, execute one GraphQL request.
        
       | tomduncalf wrote:
       | 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 wrote:
         | I 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 wrote:
         | There 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 wrote:
         | I 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.
        
         | neolefty wrote:
         | For 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.
        
         | loh wrote:
         | Yeah. 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 wrote:
         | I don't really understand extendState because it seems like
         | somewhat pointless syntactic sugar for:
         | 
         | setState((previousState) => ({...previousState, foo: false}))
        
           | ui4jd73bdj wrote:
           | I would imagine it would help more with nested objects.
           | 
           | ({ contact: { address: "" } })
           | 
           | as opposed to
           | 
           | ({ ...prev, contact: { ...prev.contact, address: "" } })
        
             | loh wrote:
             | For deeply nested data, I would probably use `immutability-
             | helper`. Many deep spread operators can get ugly and
             | confusing pretty quickly, in my experience.
        
             | zeroonetwothree wrote:
             | I guess how you should handle that is ambiguous. Probably
             | best to be explicit for that reason.
        
           | loh wrote:
           | Yep! 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.
        
         | what_is_orcas wrote:
         | I 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.
        
       | TimMeade wrote:
       | Excellent! Love the extended state.
        
         | bayesian_horse wrote:
         | It's unnecessary. Just use individual useStates for each
         | property. Makes it easier to type, too. Otherwise you might as
         | well go with something like Redux.
        
           | xixixao wrote:
           | Redux is far from the suggestion, and imho unless you can
           | greatly benefit from the actions boilerplate its overhead is
           | not worth it.
           | 
           | As others mentioned useImmer is a more bulletproof solution
           | then assuming your state can always be modeled by a single
           | layer Object though.
        
             | bayesian_horse wrote:
             | I too felt like that for a long time. Eventually I got when
             | and why Redux is useful, even with the boilerplate. Also,
             | modern semantics and utilities greatly help. Redux Toolkit,
             | RTK Query, reselect and so on.
             | 
             | Actions make state more debuggable. Selectors make
             | rendering more performant and often help structure complex
             | rendering logic. Also, Redux is a way of not having to
             | define asynchronous control flow by composing components,
             | which can get really messy.
             | 
             | Granted, it's not necessary for every app.
        
               | handrous wrote:
               | I like Redux because it's portable--you can easily rip it
               | out of a React project and use it anywhere--and because
               | it provides a really nice way to separate backend-
               | talking-stuff from frontend-rendering-stuff, if you're
               | dividing up dev tasks. Nice clean place to split off a
               | library for whatever service(s) you're consuming.
               | 
               | And with Typescript, it's not a bit unpleasant to work
               | with.
        
             | 5e92cb50239222b wrote:
             | Redux Toolkit doesn't require much boilerplate anymore
             | (IMHO). I found Redux to be absolutely appalling and never
             | used it (despite having some familiarity with functional
             | programming and the ideas underlying Redux) before RTK came
             | along.
        
               | acemarke wrote:
               | Heh, glad to hear that RTK is an improvement there :)
        
           | tddhk wrote:
           | Completely agree, also individual named state hooks are great
           | for semantic reasons, they will make your code more readable
           | and more easily maintainable.
        
           | nsonha wrote:
           | the whole point of setState is that it forces you to think
           | about component state from a centralized point of view and
           | help avoid modeling it the wrong way (eg a bunch of optionals
           | instead of an union type for the whole state). If you split
           | them to a bunch of states then state becomes just mutable var
           | in a different syntax.
        
             | steve_adams_86 wrote:
             | They're much different than mutable variables with a
             | different syntax. Changing them causes your component to
             | render and reflect changes.
        
               | nsonha wrote:
               | that doesn't matter. Look at frameworks in which you have
               | states that when mutated, trigger a rerender, such as
               | svelte. Suddenly (scattered) setState calls are not very
               | different than (scattered) assignment statements
        
             | bayesian_horse wrote:
             | That's not really true. Regardless how many setters you
             | have or invoke, state only changes between renders. Of
             | course state is mutable, that's the whole point. It's not
             | mutable during the render function though. It's also easier
             | to accidentally mutate an object assigned to a const than a
             | string or number.
             | 
             | Also, the setters are usually passed around all over the
             | place, so no, I don't think the point is centralizing
             | component state at all.
        
               | nsonha wrote:
               | centralizing as in when you setState({ a: true }), you
               | also think "wait a minute is having a `state.b` still
               | makes sense, now that "a" is `true`", maybe it should be
               | setState({ a: true, b: null })
               | 
               | > It's not mutable during the render function
               | 
               | I don't think that's the point at all when people talk
               | about mutability. To me mutability is just a proxy for
               | "lacking access control", which again goes back to the
               | point about centralizing state updates. So arguing about
               | the technicality of what is mutable and what is not is
               | pointless.
        
       | strogonoff wrote:
       | A nice approach of separating concerns is having the API layer
       | provide its own, already typed hooks and hook-backed primitives
       | such as `useThing()`/`updateThing()` (or
       | `API.thing.useData()`/`API.thing.update()`, etc.), which under
       | the hood can do whatever necessary (including using shared logic,
       | API-specific access request handling) while providing common
       | primitives for tracking progress, cancellation[0] and other
       | conveniences. With TypeScript, you can ensure those hooks are
       | typed appropriately and don't require callers to annotate.
       | 
       | Compared to approaches such as
       | `setAsyncState(API.thing.get<ThingState>())`, which do seem
       | clever, the former approach may be a bit more concise, and I'd
       | argue more predictable (if I see a `setState()`, I'd rather not
       | have to do a double take and reason whether or not it actually
       | sets state where it says it would).
       | 
       | [0] The cancellation approach in the example in this article rubs
       | me wrong, because the update request is not (and can't really be)
       | actually cancelled, so the thing may be updated without GUI state
       | knowing.
        
         | loh wrote:
         | You can certainly compose a `useThing` hook (and others) for
         | more encapsulation and functionality specific to said thing.
         | 
         | We actually do this elsewhere as necessary, like with the top
         | level `Store` component. For the most part, it depends on the
         | situation and how concise/redundant (or not) you want to be. In
         | many cases it actually ends up being more concise overall to
         | simply compose `setState(readThing(id))` as needed, instead of
         | creating a specific method for every possibility.
        
       | null_deref wrote:
       | Have you thought about using react-query?
       | 
       | The second hook is given freely by 'react-query', the first one
       | is done little bit differently
        
       | tuan wrote:
       | In the design pattern mentioned at the end, there are some
       | application state that are stored in both `updateRequest` and
       | `state`, i.e. the data that is returned from the update response
       | (stored in updateRequest) and is used to extend `state`. Having
       | the same data stored in 2 different states seems error-prone.
       | Which one is the source of truth when the 2 states accidentally
       | get out of sync ? For example, nothing prevents extendState() to
       | be called again to modify the `state` to something that is not
       | the same as the value from update response.
        
         | loh wrote:
         | The API response ends up being the source of truth, in this
         | case. If you wanted to prevent the user from updating the
         | internal state while waiting on the API, you could certainly do
         | that with a check for `updateThingRequest.status ===
         | 'pending'`.
        
       | enjoylife wrote:
       | I don't know what applications they are developing but an
       | application only needing two custom two hooks, would be a quite
       | trivial one. Not to mention the hook in the article extendState
       | is basically bringing back the Component `this.setState`
       | semantics but not much else.
        
         | loh wrote:
         | These patterns are used all throughout an email app built and
         | released earlier this year: https://www.tricepmail.app
         | 
         | We'll probably open source portions of it as well eventually,
         | but first we're giving it a huge UI overhaul.
        
       | imbnwa wrote:
       | Could Hooks be construed as Facebook Engineering's attempt at
       | modeling React as an Effect monad?
        
       | lifeplusplus wrote:
       | simple few concise meaningful lifecycles methods to vague
       | handicapped overlapping infinite hooks, really outdone in the
       | name of progress.
        
       | knuthsat wrote:
       | Hooks are great!
       | 
       | Although, when I see code snippets on the internet I always
       | wonder how do people test this.
       | 
       | The fact that dependencies are declared as imports and all the
       | logic is inside the component function, it feels like you have to
       | shuffle things around just to make the thing runnable outside of
       | React.
        
         | steve_adams_86 wrote:
         | Opinion: Although you can test hooks in isolation, I find they
         | tend to be fairly primitive and testing them in combination
         | with components can be more useful.
         | 
         | In isolation, unless you have very complex hooks, it's a bit
         | like you're testing react or JavaScript themselves. When
         | testing a component which uses the hook, I find you get to test
         | the behaviour and expectations a little better - it's close to
         | a real use case.
        
         | loh wrote:
         | > how do people test this
         | 
         | You can find the tests for these hooks here:
         | https://github.com/Molecule-dev/molecule-app/tree/6e2456e216...
         | 
         | Is that what you're asking about?
        
         | HuntingMoa wrote:
         | react-testing-library provides a helper to test hooks in
         | relative isolation - renderHook.
        
         | gherkinnn wrote:
         | Unless you're talking about library-level hooks, there's rarely
         | a need to test them directly. I'd mostly consider them
         | implementation detail.
         | 
         | Testing the components themselves is ample.
        
         | bayesian_horse wrote:
         | There are special tools to "render" react components in node-
         | based test runners.
         | 
         | Of course, you need to write your components in such a way that
         | they are easy to isolate. You can also make use of composition
         | and maybe even mock out components.
        
       | goblin87 wrote:
       | For your first hook just do this instead:
       | 
       | setState({ ...state, foo: 'updated' })
        
       | twic wrote:
       | Okay so if i have:                 const read = (id: string) =>
       | API.client.get<State>(`things/${id}`).then(response => {
       | return response.data       })              const [
       | readThingRequest, readThing ] = usePromise(read)
       | 
       | And i want to read two things from the server:
       | const first = readThing('alpha');       const second =
       | readThing('beta');
       | 
       | Then both will update the same readThingRequest, right? So the
       | information in it will vary according to which one returns first
       | or something?
       | 
       | This feels like slightly the wrong scoping. Either let me call
       | the wrapped function multiple times, and get multiple metadata
       | objects:                 const [firstRequest, first] =
       | readThing('alpha');       const [secondRequest, second] =
       | readThing('beta');
       | 
       | Or push the wrapping down to the calling of the function:
       | const [firstRequest, first] = usePromise(read, 'alpha');
       | const [secondRequest, second] = usePromise(read, 'beta');
        
       | thrwy_918 wrote:
       | Something I often want to do is write a custom hook with internal
       | state that will be used by two or three components - and what I
       | really want to do is have _consistent internal state_ for that
       | hook, regardless of which component its being invoked from.
       | 
       | This obviously doesn't work with vanilla hooks, but is there a
       | way to achieve this pattern? It seems like it would be so light
       | and fast compared to a heavier solution
        
         | ngoel36 wrote:
         | Redux is a game changer here
         | 
         | https://easy-peasy.vercel.app/docs/api/create-store.html
        
         | chrisfosterelli wrote:
         | This is what react context is for. You can create helper hooks
         | to expose the context more conveniently.
         | 
         | EDIT: I threw together a small example if it helps:
         | https://gist.github.com/chrisfosterelli/2e523b4beae43f056249...
         | 
         | It's slightly overengineered in that not everything has to be
         | in separate files; I just reduced this from an an existing
         | example that was more complicated and had this file structure
         | already.
        
           | thrwy_918 wrote:
           | thank you!
        
             | chrisfosterelli wrote:
             | you're welcome! happy coding
        
         | ishjoh wrote:
         | When we need a custom hook to have consistent state across
         | calling components we have always used a context:
         | https://reactjs.org/docs/context.html#when-to-use-context
        
         | frosted-flakes wrote:
         | Yes, with Context. You will need to wrap part of the tree with
         | that Context's Provider, and then you can consume the context
         | value with React.useContext inside a component. Your custom
         | hook can also use this.
        
       | Waterluvian wrote:
       | Pedantic nit: [ readRequest, requestRead ] reminds me of one of
       | my least favourite nitfalls from Django: FileField and FieldFile.
       | Yes, the names make sense. But human brains mash these things up
       | so regularly that I never seem to be able to permanently remember
       | what's what. I'm always triping on them.
       | 
       | This also brings me to something I've been wrestling with a bit:
       | Array unpacking allows you to pick your own variable names, but
       | you also generally have to unpack everything if you want some
       | later variables. Order matters. I find this is not very self-
       | documenting and is less fun for autocomplete.
       | 
       | Object unpacking lets you pick and choose what pieces you need
       | from the hook's interface, and they come pre-named, which is good
       | but sometimes bad.
       | 
       | I think I'm settling on Object unpacking being the better pattern
       | for my tastes.
        
       | andrewstuart wrote:
       | How is extendState different to setState?
       | 
       | setState already allows you to extend the current state.
       | 
       | I don't get it.
        
         | mlnj wrote:
         | Based on your other replies I think you got it already, but
         | I'll just add it here for the sake of others.
         | 
         | Setting a state is assigning the entire state object in this
         | case:                 setState({ a: 1, b: 2, c: 3 })
         | 
         | The next time i want to update that state (not replace it
         | completely), I do:                 setState({...state, a: 4 })
         | 
         | or setState(prevState => ({...prevState, a: 4 })) `
         | 
         | for the sake of brevity, the extendState just hides the
         | boilerplate away:                 extendState({ a: 4 })
         | 
         | It's just a matter of convenience to me too. I just call it
         | `updateState` in my projects.
        
           | andrewstuart wrote:
           | As mentioned elsewhere, this is invalid code - a bug waiting
           | to show itself - you should never do:
           | setState({...state, a: 4 })
           | 
           | If all extendState is doing is concealing the underlying
           | function call, it's saving very little boilerplate and adding
           | the cognitive load and potential issues related to the
           | wrapper.
        
             | mlnj wrote:
             | Not sure what you mean by invalid code. This is working
             | code.                 import { useState } from 'react'
             | function App() {         const [count, setCount] =
             | useState({ a: 0 })              return (           <button
             | type="button" onClick={() => setCount({ ...count, a:
             | count.a + 1 })}>             {count.a}           </button>
             | )       }
             | 
             | Cognitive load adds over time and I think it's so much
             | simpler to break down such loads into smaller pieces that
             | are documented well and abstract away the
             | complexities/syntactic nuances. In short I keep forgetting
             | to spread the previous state and that led to bugs for me.
             | 
             | Edit: Yes, you are right about the stale state and that was
             | one of the primary reasons for me to not make it a pattern
             | everywhere.
        
               | andrewstuart wrote:
               | >> Not sure what you mean by invalid code. This is
               | working code.
               | 
               | It MIGHT work, but it is still incorrect code - you must
               | always use an update function if you are updating state,
               | it's not optional.
               | 
               | Let's put it another way to be clear. This code is using
               | stale state, _don 't use stale state_:
               | <button type="button" onClick={() => setCount({ ...count,
               | a: count.a + 1 })}>
               | 
               | The example you give above is incorrect code - it does
               | not work the way you think - you need to update your
               | understanding of React. React batches the useState calls
               | and executes them later - so you must be very clear about
               | the data you are using in the useState function - picking
               | up the state value from the enclosing code gives you a
               | state value that you cannot trust.
               | 
               | The ONLY reliable to way actually get the previous state
               | is to use a function as the argument to setState.
               | 
               | In he example above, you _think_ you are getting the
               | current value of count, but it _might be stale_ - this is
               | critical to understand in React and if you don 't
               | understand it then you'll be fighting weird bugs forever.
               | 
               | If you wish to use existing state, when you pass an
               | update function to setState then React ensures that the
               | previous state argument passed in is in fact the previous
               | state.
               | 
               | i.e:                 <button type="button" onClick={() =>
               | setCount({ ...count, a: count.a + 1 })}>
               | 
               | should be:                 <button type="button"
               | onClick={() => setCount(prevCount => ({ ...prevCount, a:
               | prevCount.a + 1 }))}>
               | 
               | I think that's the correct brackets but HN is not an IDE.
               | 
               | The rule is simple: if you are updating state, ALWAYS
               | pass an update function.
               | 
               | The reason I have an issue with the topic of this HN post
               | is that updating state is critically important in React
               | and you should understand how it works and do it
               | correctly, with a function call that uses previous state.
               | useState with a function call is not boilerplate to be
               | abstracted away, it's a simple and unambiguous way of
               | writing React code and is central to writing React apps
               | correctly - don't hide this.
        
       | nightpool wrote:
       | The initial writing for "useAsyncExtendedState" said "Don't use
       | extendState for everything! Use it only when you know you need to
       | merge a partial state.". However, the example doesn't use
       | "setState" at all. This feels like a gap or tension in the API
       | design--if setState doesn't get used in practice, why force users
       | to include it? Instead, I think the default react setState
       | pattern is much better, since it allows you to easily set *or*
       | extend your state depending on your usecase:
       | const [state, setState] = useState({});                  //
       | replace         setState({foo: 1, bar: 2});                  //
       | extend         setState(currentState => ({...currentState, foo:
       | 1}));
        
         | loh wrote:
         | In the examples, `extendState` is used because the API returns
         | only the updated props when updating.
         | 
         | Your preference for only `setState` is definitely warranted.
         | The source is available for this reason, and it's pretty
         | compact. You can quickly remove the `extendState` portion if
         | you want.
        
           | [deleted]
        
       | flippinburgers wrote:
       | Just use redux.
        
       ___________________________________________________________________
       (page generated 2021-12-17 23:00 UTC)