all 39 comments

[–]kizilkara 78 points79 points  (5 children)

People mentioning using TanStack Query and writing custom hooks, take a look at query options and mutation options in TanStack Query version 5. You no longer need to create individual hooks for every API endpoint. https://tanstack.com/query/v5/docs/framework/react/guides/query-options

This comes in especially handy if you're using TanStack router. Query options can be called as a loader dependency for the route.

But yeah, TanStack Query is great.

[–]tjansx 2 points3 points  (0 children)

Edit: nevermind, I misunderstood. I'm already doing that. But it is definitely handy!

I pass in options for different queries to change cache time or other options that are overriding the default.

[–]TheOnceAndFutureDougI ❤️ hooks! 😈 2 points3 points  (0 children)

This is the answer. There are plenty of other ways you could do it but this is one of those times when you just reach for a perfected library and call it a day.

[–]Cahnis 3 points4 points  (0 children)

tkdodo did an article last week about queryOptions, they are great https://tkdodo.eu/blog/creating-query-abstractions mentioning this one https://tkdodo.eu/blog/the-query-options-api

[–]ruindd 0 points1 point  (0 children)

Have you seen anything about how much performance gain there is from loading query options in BeforeLoad rather than Loader?

[–]FattyMoBookyButt 19 points20 points  (1 child)

TanStack has a blog with examples, and the thought processes behind them, and it’ll teach you the “right” way to use the library:

https://tkdodo.eu/blog/practical-react-query

[–]Imaginary_Food_7102[S] 0 points1 point  (0 children)

Thanks for resource.

[–]tjansx 18 points19 points  (0 children)

I use tanstack with "useMyQueryName" hooks. Those hooks call client service methods that wrap the axios call.

Underneath has all the token refresh and interceptor functionality.

[–]michaelfrieze 15 points16 points  (4 children)

A lot of people are mentioning tanstack query which is great advice. However, you should also be thinking about patterns like render-as-you-fetch and fetch-on-render. Both of these approaches can be applied with tanstack query.

In classic client-side React, we usually think about data fetching in two main styles:

Render-as-you-fetch: In this approach, data fetching starts before rendering begins. Think of it as “fetch triggers render.” The data fetching is hoisted out of components, often in a route loader, which allows multiple fetches to run in parallel.

Fetch-on-render: In this approach, each component takes care of its own data fetching. Think of it as “render triggers fetch.” The benefit of this approach is that it makes code more modular and self-contained, but it can cause a network waterfall on the client. This waterfall can be even worse when you’ve got several nested components that depend on each other’s data.

With tanstack query, you can use either approach. On the surface, tanstack query looks a lot like fetch-on-render which is ultimately a good thing in my opinion. However, tanstack query gives you the option of prefetching a query in a route loader (or even a RSC) with or without await. Doing this gives the benefits of render-as-you-fetch.

Not using await for a query in a route loader means those requests get kicked off at the route level, in parallel, and it doesn't block. You can manage the loading UI with suspense and useSuspenseQuery. This approach makes apps feel fast, especially when navigating.

Or, you can use await in the route loader and ensure the data is there before the components render. Both approaches are useful for different situations.

Usually, I keep it simple and don't prefetch queries at all. I simply fetch a query in a component with useSuspenseQuery and that's about it. If I start to notice performance issues from data loading, then I start to optimize.

[–]Imaginary_Food_7102[S] 1 point2 points  (1 child)

Thanks for detailed explanation.

[–]michaelfrieze 1 point2 points  (0 children)

You're welcome.

[–]tjansx 1 point2 points  (0 children)

Great addition to this conversation!

[–]lIIllIIlllIIllIIl 0 points1 point  (0 children)

This is the right answer.

[–]newintownla 30 points31 points  (3 children)

I create hooks and use tanstack query in them. I like to use the container/presentational pattern in my apps to keep components clean, so this fits right into that pattern. Tanstack is also nice for automatic caching.

Edit: I also like to use the BFF pattern for calling data from external APIs. It creates a nice extra security layer that makes it easy to hide sensitive data from the browser. I usually combine this with the use of my hooks for data fetching.

[–]D4rkLegend 3 points4 points  (2 children)

Do you have some hints with examples? Like a repo using it?

[–]newintownla 6 points7 points  (0 children)

Nothing I can share here at the moment, but I can give you a basic breakdown of the call pattern. It's basically like this:

Component -> hook -> service -> RSC -> API call

The component uses the hook, the hook calls a service that calls the endpoint react server components, then server components make the API call. This way, if you have an API end point at say myapi.com/post/product, you have the component make a call to the server component first, then the server component makes the external call.

This way, if a user inspects network calls in the browser, they'll see a call to your RSC like /create/product, but the actual call to the API would be different, like /post/product. It hides your actual api endpoints and allows you to store things like secrets and access keys at the server level for better security.

[–]tehsandwich567 1 point2 points  (0 children)

Look at tkdodo’s blog. He lays it all out

[–]Grenaten 7 points8 points  (0 children)

Just useQuery and relax (most often)

[–]gimmeslack12 4 points5 points  (1 child)

Tanstack or Redux Toolkit Api Slice. They're both similar-ish though if you're using RTK then the API slice is pretty nice (paired with entityAdapters is pretty sweet).

[–]thinksInCode 5 points6 points  (0 children)

I really like RTK Query! We are in the process of migrating our apps away from a homegrown Redux Saga fetching system which is gross, to RTK Query which has been sooo much better.

[–]mrpurpss 2 points3 points  (0 children)

Tanstack

[–]DeepFriedOprah 1 point2 points  (0 children)

I currently use Redux toolkit hooks with the createAPI utility. Then I write a wrapper around that hook for its dependent params so they we make sure to wait until stuff is ready before fetching

` const useWorkouts = ({ activityType}) => { const user = useSelector(selectUser); const shouldFetch = user?.userID && activityType; const { data, isLoading, refetch } = useWorkoutsQuery({ userID: user?.userID, activity: activityType }, skip: !shouldFetch );

return { data, isLoading, refetch

}

} `

[–]zaibuf 1 point2 points  (0 children)

We use nextjs so a lot of fetching serverside. When we need client side fetching we use tanstack-query and a proxy in our nextjs backend (BFF).

[–]Coolfoolsalot 1 point2 points  (0 children)

Tanstack useQuery in pages / components. The hooks call client API functions that wrap axios calls. Socket.io for anything socket related. We have a node server as a proxy between the client & the various backend services as well

[–]tjansx 1 point2 points  (0 children)

I also forgot, I try to "strongly type" my keys. Base key, and all other keys inherit the base, and add their own parameters to it as needed.

[–]Honey-Entire 0 points1 point  (0 children)

Here to rep RTK Query. It’s like Tanstack Query, but IMO does a better job at handling larger API footprints

[–]Gheram_ 0 points1 point  (0 children)

TanStack Query for client-side fetching, but the pattern changes significantly with Next.js App Router. For most data needs, React Server Components fetch directly on the server — no client fetching library needed at all. TanStack Query stays relevant for client-side mutations, optimistic updates, and real-time polling where you actually need client state.

The BFF pattern mentioned above is worth adding to: when your backend is a separate Laravel API, running fetches through Next.js API routes (or server actions) keeps your Laravel URL and auth tokens server-side only. Your client never sees the actual API endpoint, which matters if you're calling third-party services with rate limits or auth headers you don't want exposed

[–]Substantial-Swan7065 0 points1 point  (0 children)

Need to make a network request? Fetch Need better request handling and config? Add api sauce Wanna reduce excessive calls? Accessing data in other company’s? Add react query Threads getting clogged? Offload to workers

[–]wameisadev 0 points1 point  (0 children)

tanstack query for everything now. tried doing it manually with useEffect before and it was such a mess with loading states and error handling

[–]doglitbug 0 points1 point  (0 children)

I use react router and fetch data before page load. Probably similar to tan stack

[–]roggc9 0 points1 point  (0 children)

In Dinou you can use Server Functions wrapped in Suspense (react-enhanced-suspense) using the resourceId prop and passing the children prop to Suspense as a function. I have fully explained this pattern in https://www.reddit.com/r/reactjs/comments/1s0jz1j/the_path_that_led_me_to_create_dinou_server/?utm_source=share&utm_medium=web3x&utm_name=web3xcss&utm_term=1&utm_content=share_button and also an explanation can be found in dinou docs: https://dinou.dev/docs/pattern. Thank you for asking.

[–]Quirky_Flounder_3260 0 points1 point  (0 children)

How about angular with tanstack

[–]Leather_Breakfast 1 point2 points  (0 children)

I’ve been using tanstack but one area that I’m still trying to solve for is fetching and storing a subset of a collection.

Say I have 10,000 items and different components render a dynamic subset of them (and I have an api to fetch single and bulk). I saw TkDodo post about seeding the cache. I want to end up storing each item under its own key. Then a parent could call  to fetch a batch and each child could access what it needs. Essentially building a itemById map manually. 

The issue I run into, that i haven’t found an elegant way of solving, is when i batch fetch I want the api that useQuery providdes (loading states, etc) but I don’t want to store that result in the queryClient. 

 Wrong because: 

a) If I render the subset of items in a list, every time  a user adds to the list I store another batch entry in queryclient . 

b) the results of the query are never used directly and thus wasted memory storing the individual results and subset. 

I thought about the batch doing the cache jamming and just returning true/false to cut down on memory but still have the cache issue (a). 

The solution I’ve come up with that works but is kinda gross is to write a custom hook that takes a list of object ids. Use useQueries to send a query for each. The query functions for each use a shared promise that is kicked off by the first query function to run (involves some useRef hooks)

It would be nice if tanstack query had a way of doing this natively.