Post

If you are totally against setting state in a useEffect, how would you recommend doing this kind of thing? Assume a SPA.

3 reposts63 likes

If you don’t want to use tanstack query, I like to bundle this into a useAsyncMemo hook, so that the code isn’t repeated everywhere. But replacing it with tanstack query will give you caching / reuse, retries, loading statuses, timeouts, etc, which is very handy.

I feel like people mainly have a problem with people updating state from an effect ran that was triggered by another state change. This is basically just an onMount.

You would have to track if component is mounted. In React 19 it could likely be done differently. Would be great if React Compiler would allow async client components…

Funny enough you helped inspire this. You made a video years ago about Recoil + Module Federation. That was the starting point for my research to find composable state management (+ data fetching)

For folks proposing react-query, it should not be a valid answer to this post since internally react-query does exactly what this code does. If react-query is allowed then there's nothing wrong with this code snippet.

Not a React answer but the principle is the same: I like to think about data fetching as a mapping of a reactive state => reactive response. Newly introduced Angular's resource, SolidJS resource (minimal APIs) and TanStack Query (a real lib) lets one think in the "mapping" mental model.

By creating a parent component and suspense boundary. We greatly encourage/direct folks towards using a suspending, cacheable data loader (TSQ) and to place their suspense boundary in a direct parent of the consuming component. Suspense is the important recommendation imo.

I'm not concerned with your recent sample nor this exact code; I'm concerned with what's added after. How to avoid waterfalls after 6 months of me not looking at it. How to ensure devs have data loading options top of mind for great UX depending on now their screen's features grow.

Yeah, folks are getting from something that state setting in a useEffect is bad, period. So I end up getting even when it's something like this. I certainly agree sync setting is bad, but data loading like this is fine, IMHO. RQ is certainly nicer, but not required.

But honestly, I think this is a bad pattern anyway. Loading data in a React component that will render it is bad for the end user because you have to show them a loading state. It's more buttery smooth to find clever moments to prefetch and only use React once you have the data you need.

Technical communication is hard because technically that set state is not “in” the effect. The effect just calls the promise. But you can’t blame a lot of people for thinking this *is* inside the effect since the code is between the brackets haha.

This isn’t in the effect, it’s in the promise callback, so it’s fine but still has footguns because of race conditions. Better to use use().

There are 3 use cases for fetch: - get data (GET) - refresh data (GET) - change data (POST, PUT, DELETE)

Starting in 19, you shouldn’t need to fetch in an effect for any of these use cases. Initial GET should be cached and read in render with use(). Refresh GET should clear the promise cache to trigger with use() again. POST/PUT/DELETE mutations should be done in event handlers with actions.

Cool, very curious how to implement revalidation after mutation. Is it documented? I have not found anything about that (but maybe I’m blind 😅)

I didn't know about the clearing promise cache option. Good to know it! It definitely drops most of the needs to setState in an asynchronous manner just because of a fetch call

Yeah, it’s unfortunate but right now I think it’s probably better to swing too far to “never” than to set it directly in the effect

Main reason why I like the approach of using loaders in remix/ react router. Both on the server or client depending the need. It saved me to keeping this useeffect boilerplates

I don't think it violates a rule. There is just this new info out there somewhere that has folks simplifying it to; never set state in a useEffect.

For sure. I'm just seeing folks saying "setting state in any way in a useEffect is bad", and I'm curious as to 1) why, and/or 2) where that information is coming from.

I think that is more paranoia from seeing bad code inside useEffect that tanks performance like 2 useEffects updating states that are in each other's dependency array or using useEffect to set a new state that should be a simple derived variable with maybe a useMemo.

I would probably move the data call to its own custom hook, and do something like `useComponentData()` when setting the initial state. But I also probably wouldn't reject a PR that included the above, either.

But also, there's this lib called react-queries or something. Not sure if it's any good and it's also quite heavy for doing a task as simple as data fetching but maybe check it out idk 🤷‍♂️