Should you avoid having the queries inside Components?
Kind of a silly question, but if I don't do that, when I have a frailly complex page with lots of queries, the file looks pretty convoluted.
And I'm not sure if I should be tying my components to queries, that kind of makes them less generic?
55 Replies
If a component can work with any set of data then you should treat it as such but making things generic for the sake of "generic-ness" i guess ? is the wrong way around it
I'm still trying to work this out myself but the way you compose your data flow is what determines how and where you stuff these queries
Even where you stuff them can be important since you have to ask will it be ready by then? etc
I really recommend the react query blog by tkdodo (team member on rq)
https://tkdodo.eu/blog/practical-react-query
Practical React Query
Let me share with you the experiences I have made lately with React Query. Fetching data in React has never been this delightful...
Ill mention what Im doing which seems to work and is an approach I've been happy with. Any page in my app that requires loading does it at the top-level component for the route. So I'm not doing like separate rest-style calls. This compoent then passes everything down normally through props and displays the loading state while its waiting. I don't use SSR because otherwise the user sees a white screen while the site is loading. Even though it might be worse for SEO the page just looks like its not working when I use SSR so I avoid it. The last trick is that I set up a specific trpc router for Vercel caching. So things like the home page should load pretty much instantly regardless of cold starts, etc
the only exception to this is components that do their own loading (such as autocomplete-style search boxes)
cant say if this is right or optimal, but it is what i do. and the site seems really fast and the code isnt too hard to work with
and if i want i can turn SSR on for any page in just a couple minutes since im already loading things at the top level component (im not using the app dir)
hope that helps
in other words, "use your better judgment"?
For example, say I have a giant logic for 1 component that I won't use anywhere else, then it would make sense to just extract it to its own file and call it a day
But on the other hand, if it can be generic, then try to make it generic
I understand this approach, but I'm currently working with an app that has to load a lot of data and has a lot of mutations going on in just one page. Think of it as a multi-step form.
If I have everything at the top level, I'll be passing a bunch of props. And somehow, we always end up needing useEffects to control the defaults for the form.
In any case, the top level component is always very bloated and feels like it's harder to work with it
I like Theo's approach of "Let's not separate everything that doesn't need to be separated", but that isn't working for our software anymore, I feel like
I’d be interested to see your code. My top level component is very simple. And to be clear I do the mutations in the components that’s deal with them. But I leave the page loading at the top level
My revolves around a few very complicated forms that involve previews and other dynamic features and this approach still works for me
This is the worse one
And the onSuccess and onError options for the queries make everything even more bloated.
But it's a huge multi-step form with some arrays within it. It's complex
ok do yourself a favour here and don't destructure the
useQuery
and useMutation
objectsok that is already a good point
didn't even think about that
Not trying to compare here but this looks simpler than my form. I agree on not destructuring
Can I see how your top level component looks?
I’m not at my computer so I can’t take a screen shot. My f form has a lot of the same problems but I was able to simplify a lot of them by keeping the whole query in 1 route
Also, there's a lot of weird stuff going on with the types and the optionals because I'm still refactoring what the junior did, so ignore that
fwiw i'd remove the mutation
onSuccess
handlers in favour of mutateAsync
in the submit handlersYeah that’s fine.
though depending on you setup that may not be the best idea
^ I also do this as well
works well for us bc our dialog + form logic is all designed to be used with async so we get loading/error indicators automatically
This way, I wait for the mutateAsync and then call the things that are inside onSuccess?
ya
Smart, I like that
the query's
onSuccess
is debatable - i don't like it but it does the jobIt indeed is.
I might start avoiding using it to be honest
I’m in my car but I can post some code and screen shots later if it helps
@brendonovich but what would be your opinion on the original question?
Sure, I always like to see how other programmers do stuff
i wouldn't say having queries inside components is bad, but there's definitely situations where lifting the responsibility of data fetching can be useful
like we have an Explorer component that can render data from
search.paths
or search.objects
queries, which are executed at a route-level above the component itself
multi-stage forms are always a tricky beast thoYeah
if i were making one i'd hold the state of the form at the top-level, split each stage into a separate component, and then do the necessary data fetching in there
And correct me if I'm wrong, but having queries inside a component is a really great way to deal with queries that depend on other queries, no?
if you're fine with having a waterfall of queries then sure yeah
Yes! That's the approach I went with in this case
oh don't forget about a top-level context that all the stages can consume instead of passing props
Always
Sometimes, it has to be a waterfall
oh ofc, waterfalls can be fine
god knows we have them
especially without graphql
one day 🙏
For example, I have a query to bring all the clients to a Select, and when you select the client, it then queries the new options for the Consultants
Good, thank you
And thank you all for the other ideas on how to avoid bloating the code too
is this form multi-page or is like each field depends on the value of the previous one?
Both
Some fields depend on the previous, but it's a multi-step form
Ah gotcha
@deforestor so this is one of the central forms of my app
its for running tournaments and this form lets you change the settings
each tab has a bunch of different settings which vary from very basic to relatively complex
the create form is largely the same, but it includes a preview and doesnt look at the existing tournament to autofill form fields:
the top-level component of this route basically just handles the loading and passes it to a child component:
this means that the child can always assume that it has all the data loaded
this route loads everything this page needs from the db
from there my app has a couple wrapper layers for layouting purposes and then it gets to the main form
the form has a lot of logic but i break it up into tabs and each tab has its own component
i pass the react hook form context down and each form is responsible for one or more of the fields
all of my mutations are done thru mutateAsync(). for my app i show a little toast popup on both error and succesful completion
both are done with a try / catch in the form submission handler
thats how i have things set up anyway. hopefully that helps
would you happen to have this deployed somewhere for use ?
True Finals
Tournaments: now on easy mode
click 'start a tournament'
wowie thank you, looks amazing btw
ty appreciate it
hopefully releasing in july
This is a pretty cool website
I'm working on something that I hope to be useful for all of us devs, I can't wait to release it
Thanks a lot. And hope it goes well for you
So, I finally got to try this and weird things are happening.
For some reason, if I use mutateAsync, then use the
await mutateAsync(data)
, it takes a really long time to finish on the front-end
- I've already checked the server, everything there occurs instantly, so that's not the problem.
- I thought it could be a cache problem, but setting cacheTime: 1
and making a random mutateKey didn't change anything.
- I've also tried to use a useCallback in case something weird was going on with the state, but nothing changed