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
Keef
Keef13mo ago
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
Keef
Keef13mo ago
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...
Tom
Tom13mo ago
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
deforestor
deforestor13mo ago
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
Tom
Tom13mo ago
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
deforestor
deforestor13mo ago
This is the worse one
deforestor
deforestor13mo ago
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
Brendonovich
Brendonovich13mo ago
ok do yourself a favour here and don't destructure the useQuery and useMutation objects
deforestor
deforestor13mo ago
ok that is already a good point didn't even think about that
Tom
Tom13mo ago
Not trying to compare here but this looks simpler than my form. I agree on not destructuring
deforestor
deforestor13mo ago
Can I see how your top level component looks?
Tom
Tom13mo ago
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
deforestor
deforestor13mo ago
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
Brendonovich
Brendonovich13mo ago
fwiw i'd remove the mutation onSuccess handlers in favour of mutateAsync in the submit handlers
Tom
Tom13mo ago
Yeah that’s fine.
Brendonovich
Brendonovich13mo ago
though depending on you setup that may not be the best idea
Tom
Tom13mo ago
^ I also do this as well
Brendonovich
Brendonovich13mo ago
works well for us bc our dialog + form logic is all designed to be used with async so we get loading/error indicators automatically
deforestor
deforestor13mo ago
This way, I wait for the mutateAsync and then call the things that are inside onSuccess?
Brendonovich
Brendonovich13mo ago
ya
deforestor
deforestor13mo ago
Smart, I like that
Brendonovich
Brendonovich13mo ago
the query's onSuccess is debatable - i don't like it but it does the job
deforestor
deforestor13mo ago
It indeed is. I might start avoiding using it to be honest
Tom
Tom13mo ago
I’m in my car but I can post some code and screen shots later if it helps
deforestor
deforestor13mo ago
@brendonovich but what would be your opinion on the original question? Sure, I always like to see how other programmers do stuff
Brendonovich
Brendonovich13mo ago
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 tho
deforestor
deforestor13mo ago
Yeah
Brendonovich
Brendonovich13mo ago
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
deforestor
deforestor13mo ago
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?
Brendonovich
Brendonovich13mo ago
if you're fine with having a waterfall of queries then sure yeah
deforestor
deforestor13mo ago
Yes! That's the approach I went with in this case
Brendonovich
Brendonovich13mo ago
oh don't forget about a top-level context that all the stages can consume instead of passing props
deforestor
deforestor13mo ago
Always Sometimes, it has to be a waterfall
Brendonovich
Brendonovich13mo ago
oh ofc, waterfalls can be fine god knows we have them especially without graphql one day 🙏
deforestor
deforestor13mo ago
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
Brendonovich
Brendonovich13mo ago
is this form multi-page or is like each field depends on the value of the previous one?
deforestor
deforestor13mo ago
Both Some fields depend on the previous, but it's a multi-step form
Brendonovich
Brendonovich13mo ago
Ah gotcha
Tom
Tom13mo ago
@deforestor so this is one of the central forms of my app its for running tournaments and this form lets you change the settings
Tom
Tom13mo ago
Tom
Tom13mo ago
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:
Tom
Tom13mo ago
Tom
Tom13mo ago
the top-level component of this route basically just handles the loading and passes it to a child component:
Tom
Tom13mo ago
Tom
Tom13mo ago
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
Tom
Tom13mo ago
Tom
Tom13mo ago
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
Hehe
Hehe13mo ago
would you happen to have this deployed somewhere for use ?
Tom
Tom13mo ago
True Finals
Tournaments: now on easy mode
Tom
Tom13mo ago
click 'start a tournament'
Hehe
Hehe13mo ago
wowie thank you, looks amazing btw
Tom
Tom13mo ago
ty appreciate it hopefully releasing in july
deforestor
deforestor13mo ago
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
Tom
Tom13mo ago
Thanks a lot. And hope it goes well for you
deforestor
deforestor12mo ago
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