how to revalidate data?

Router.refresh() purges all the caches if I understand correctly. How would you invalidate a cache after running a mutation using trpc on a client component?
103 Replies
iDarkLightning
iDarkLightning12mo ago
make a request to an API route to invalidate a tag or a path :) or in your trpc mutation (if its running on the same server) revalidate it
shikishikichangchang
Wdym by make a request to an API route to invalidate?
iDarkLightning
iDarkLightning12mo ago
You want to invalidate items in Next's data cache?
shikishikichangchang
I’ve fetched a list of items in a server component And the server component has a client component The client component creates a new item How do I show the updated list without a hard refresh
iDarkLightning
iDarkLightning12mo ago
You need to make an HTTP request to the Next server that calls revalidatePath() from your client component
shikishikichangchang
Wow that seems unnecessarily complicated 🥲 Do you have a good understanding of the 4 types of caches
iDarkLightning
iDarkLightning12mo ago
That's an excellent question
shikishikichangchang
I’m so confused after reading the docs
iDarkLightning
iDarkLightning12mo ago
If you ask questions I can try to answer them
shikishikichangchang
It seems like a lot of people are as well from the GitHub threads I’ve read How do you use the app router with t3 is my question Previously without the app router, i define everything in the trpc router files
iDarkLightning
iDarkLightning12mo ago
Julius M's Notion on Notion
tRPC using Next.js App Router
tRPC supports the new App Router out of the box, but there are multiple ways you might want to utilise it for your projects depending on your need and project structure. In this article, we’ll present all the possible ways and also showcase some of the experimental things we’ve been working on.
shikishikichangchang
And then just call them in componentS
iDarkLightning
iDarkLightning12mo ago
read this first
shikishikichangchang
What does this mean If it’s defined why would need to wrap it around a suspense boundary Thanks for sharing. I read all of it. Seems like most things are still in the experimental stage? From what I can tell, using t3 there’s no stable and easy way to 1. Mutate on the client and revalidate the data fetched in server components 2. Fetch on window focus if the data were fetched in a server component 3. Do all the fancy cache stuff that NextJS offers 4. Use server actions (what’s the benefit of this anyway compared to trpc mutation, is it no JS?) 5. Stream data in with which react query can use
iDarkLightning
iDarkLightning12mo ago
It's defined because it's in a suspense
iDarkLightning
iDarkLightning12mo ago
Read that as well
shikishikichangchang
I see Let me read that I read it So it’s an “initialData” approach that solves the roundtrip problem of “1” since it off loads the logic to the client side so if I invalidate the cache, it will refetch. It also solves “2” since it’s using client side. It also solves “5” since I can render other components and wrap the client component in Suspense and once it’s ready, react query can be used due to initialData. Am I right so far? There is the downside that I have to write a similar query for every query tho
iDarkLightning
iDarkLightning12mo ago
Yes, pretty much I'm not sure what you mean
shikishikichangchang
Rather than use query once in the client component, we are fetching once on the server and also using query on the client Also this means that I’ll have to mainly use client components (only the fetching data is in server components, not the rendering) Should I be using any of the NextJS cache stuff And server actions To get around this, I read Julius’ docs again and it seems like I can use “useSuspenseQuery” together with the experimental “ReactQueryStreamedHydration” to achieve the same behavior In conclusion, 1. Server actions are experimental with t3/trpc 2. Nextjs cache are experimental with t3/trpc 3. We can use server components two ways: a) using useSuspenseQuery with initialData b) using useSuspenseQuery with the experimental ReactQueryStreamedHydration Question: 1. Are server actions better than trpc mutations? If so, how?
iDarkLightning
iDarkLightning12mo ago
1. Server Actions can be used pretty normally, as in you can use the vanilla trpc client with server actions 2. Honestly...just don't use the nextjs data cache for dynamic web apps imo. especially if you have an auth layer 3. If you're using initialData then you don't need a suspense query 4. Mainly using client components if all your data is fetched on server is perfectly fine 5. Server actions are not inherently better, but server actions using the native form should work without JavaScript, however enough of your app probably won't to where this isn't very useful
shikishikichangchang
Can you elaborate on 1? How do I do that do you have an example? Agree with 2 lol I don’t think what you said about 3 is necessarily true? If I have a page that have a server component in it, I may want the page to load immediately and have the SC streamed in, so wrapping it around suspense is useful since it doesn’t block the page load I think 4 makes sense except that we don’t get to take advantage of the low payload size of server components? Agree with 5 But for 5, does using server action mean we require less JavaScript fetch (for the trpc mutation)?
iDarkLightning
iDarkLightning12mo ago
Well yes, next app router does implicitly use suspense through the file structure thiugh Yeah, it might be a bit finicky but it depends. If you really care, you can extract just the data rendering to client components, and it shouldn't be the biggest deal depending on what you ship to the client I don't have an example at the moment, but nothing is stopping you from creating a caller/a vanilla client and just calling the mutations, if that makes sense
shikishikichangchang
Thanks a lot for clarifying all these things, I appreciate it How did you find the two links you shared with me by the way?
iDarkLightning
iDarkLightning12mo ago
The first one is pinned in the trpc discord, the second one was written by Josh, who's fairly active on this server
jack
jack12mo ago
The approach in the blog post doesn't feel as ergonomic as what I generally expect from tRPC 😦 Also we run into the issue where "just fetch where you need the data, it de-dupes" is still broken here? If you need data in a layout and a page that belongs to the layout, this hits the server 2x ? Really hoping for a nicer fix here where the trpc call on the server has parity with the next fetch patch and all their cache stuff. Unless I'm horribly misunderstanding something
iDarkLightning
iDarkLightning12mo ago
if you don't re-create the client then why would it be broken?
jack
jack12mo ago
Because it doesn’t use the next cache When I call a procedure from layout and a page that belongs to the layout, it runs on server 2x
iDarkLightning
iDarkLightning12mo ago
you're going to have to be more specific than "the next cache", there's like 4 different caches
jack
jack12mo ago
Sorry. It doesn’t use a cache period At least in the ct3a setup I’ve installed It has to be wrapped in reacts cache fn afaik, which is not very intuitive at all
iDarkLightning
iDarkLightning12mo ago
interesting, you're right I was under the impression that trpc uses fetch, next's overwriting of the global fetch would just be used instead
jack
jack12mo ago
Yea.. it feels like every day I’m switching between really enjoying app router and then hating it Never spent so much time trying to decide how to structure my components for a page
iDarkLightning
iDarkLightning12mo ago
im going to have figure this out lol 😭
jack
jack12mo ago
It’s really frustrating and now I’m thinking of going back to remix lol I like tRPC. I like app router. I’m not smart enough to figure out the hard cache/invalidation stuff In pages ct3a, everything is easy and solved. In app router it feels, as is precautioned in the setup, unfinished
lanc3
lanc312mo ago
Having the same problems here as well, it seems that app router is really only viable for simple, non-interactive apps, and is just too tedious to work with for certain features. Really liked server components, but in the end I'm probably just going to wrap my root component in "use client" to ignore it pretty much because of hard requirements like global state
iDarkLightning
iDarkLightning12mo ago
I do want to ping @Josh here, interested if you have anything to say on this?
Josh
Josh12mo ago
Reading I actually need to rewrite this, I've updated my patterns quite heavily I'll rewrite it later, I'm on vacation rn so not sure when I'll get it up but basically, I've found the best approach is to get all your basic page data in the rsc, then use mutations to call router.refresh in your onSuccess And wrap the router refresh call in a transition and watch the is transitioning variable to track when the refresh is done This outlines it a bit
Josh
Josh12mo ago
GitHub
GitHub - GentikSolm/t3-app-dir: t3 app dir boilerplate
t3 app dir boilerplate. Contribute to GentikSolm/t3-app-dir development by creating an account on GitHub.
Josh
Josh12mo ago
Read the readme for more info there
iDarkLightning
iDarkLightning12mo ago
See the fact that you have changed basically your entire set up in the span of like less than 2 months doesn't inspire confidence in app router 😭 Also refreshing all of the data for every change seems like a bad idea?
Josh
Josh12mo ago
Depending on the change yes Hahahaha I also have a very "break often fix fast" mindset
iDarkLightning
iDarkLightning12mo ago
But you don't get to choose do you
Josh
Josh12mo ago
Wym I'm our only developer so I change everything at my own whim lmao Hence why I can move so fast If you're changing something individually my pattern has been to get data in rsc, then optimistically update in onSuccess. Most of my routes are already dynamic so I don't need to revalidate
jack
jack12mo ago
I’ve been working in the same page in my app for 2ish weeks now and I swear I’ve tried every configuration of client/server and data load architecture So I’m experiencing the same thing with having to change patterns often
iDarkLightning
iDarkLightning12mo ago
lmfao I meant you don't get to choose what changes you use router.refresh for
Josh
Josh12mo ago
The way I look at it is that this stuff is wildly bleeding edge. We are the early adopters of this stuff and we have to figure out the best patterns for this ourselves and that's a risk/ work in willing to take on So I'm totally fine with having to change often and figure out stuff on my own cause I think it's fun and interesting Well what I said still applies, depending on how you use refresh changes you're other data syncing patterns and since I'm solo I choose which pattern to use when and where to test new stuff out I'll say this, my entire site is built with app dir and I've been extremely happy with it with this in mind. If you don't have this mindset it's going to be much more difficult Another way to think of it is that app dir is crazy new and freshly released in the broader scope of things. It's going to take time for stuff like trpc to really catch up and figure out how they want to handle things like RSCs Thank you for tagging btw, this is very much my favorite cup of tea If I'm reading this correctly then yes it's a misunderstanding. If you have a dynamic layout that prefetches data, you can tell your page to not fetch on load saving you the duplicated request
jack
jack12mo ago
But there’s no native way to share data from layout downwards a la SvelteKit/Remix Shit I’m tired af spelling “no” as “know” smh
Josh
Josh12mo ago
Yeah just use a context I actually do that in my app
jack
jack12mo ago
Yea this goes back to my point though, there’s workarounds for a lot of these things, but their recommended way in this case is “just fetch where you need the data, we de-dupe” which is just not really always applicable unless you’re using their fetch I largely agree with you on the “it’s bleeding edge, expect shit to not be nice” It just feels largely counterproductive for me to bother with /pages right now, knowing my curiosity I’ll wanna rebuild in app router as soon as I ship. But the app router experience is frustrating so we’re just in an unfortunate grey area right now
Josh
Josh12mo ago
Also When I'm sober I can help much more lmao
iDarkLightning
iDarkLightning12mo ago
The only real to invalidate data is to pass initialData to client components and use react query is basically what I'm saying. revalidateTag is basically not an option, and if we don't use react query we will have to use refresh in every scenario which might not be the best idea
jack
jack12mo ago
I was thinking of just defining an object that looks like a tRPC router. Write some sort of thin wrappers that make mutations server actions, and queries just regular functions lol
Josh
Josh12mo ago
If you can't use refresh I'd just optimistically update + revalidate
iDarkLightning
iDarkLightning12mo ago
I feel like I'm missing something Revalidate how
Josh
Josh12mo ago
Path I think if you revalidate path then next time the user visits it will re run That needs testing though Again, to be fair, I'm currently pretty drunk and on vacation so I'll need to do a bit more digging when I'm back
iDarkLightning
iDarkLightning12mo ago
but then I need to use server actions and we're using an external API and yada yada I think your old setup probably just works best for us lol
shikishikichangchang
@iDarkLightning what does @jack mean what he said the fetching is gonna happen twice on the server? Why would it happen twice? Here
iDarkLightning
iDarkLightning12mo ago
If you do a fetch in different components that are different points in the component tree, next is supposed to dedupe the requests It doesn't seem to work with the vanilla trpc client, at least with the link set up on ct3a
shikishikichangchang
I see What’s a use case that you would need to fetch the same thing on different components (in one tree)?
iDarkLightning
iDarkLightning12mo ago
you fetch the user in the layout to do an auth redirect, you fetch it somewhere else to display user data
shikishikichangchang
Ok Usually if I need that user data so often I’ll just add it to the user session I’m pretty sure useSession for nextauth dedupes
iDarkLightning
iDarkLightning12mo ago
use session is client side
shikishikichangchang
Yea Maybe I haven’t come across a use case where I need more user info than the basic ones But say in the layout page I’ve requested the user data once, I just store it in the augmented user object and useSession will give me what I need from there on
iDarkLightning
iDarkLightning12mo ago
Just anytime you want to render data from the same request in two components
shikishikichangchang
Yea but I can’t think of a use case where that’s needed Well written components should do one thing, isn’t that why we have separate components?
iDarkLightning
iDarkLightning12mo ago
Two different things can rely on data from one fetch I'm a bit confused this is a very very common thing lol
shikishikichangchang
I’ve never come across it Usually each component fetches the data that it needs For example say it if I have a dashboard and there’s a Stats component, and there’s a list of users that belongs to the project (UserList component) The way I do it is id query for the stats in the stats component, and query for the user list in the UserList component
jack
jack12mo ago
A layout that shows a users profile picture and username, and the page shows other data that depends on user data for example
iDarkLightning
iDarkLightning12mo ago
For example let's say you have a multi tenant app, and you need the org info in several places
jack
jack12mo ago
This is exactly the pattern that causes this issue
iDarkLightning
iDarkLightning12mo ago
I think I was slightly unclear, it's not just the same data**
jack
jack12mo ago
The biggest culprit is that, unlike other frameworks like SvelteKit and iirc Remix, you can’t pass data from layouts downwards as a feature of the framework. Next/Vercel recommends just refetching because it’s de-duped
iDarkLightning
iDarkLightning12mo ago
It makes one network request instead of 30
shikishikichangchang
You can just augment the user object. User is the only use case I can think of
iDarkLightning
iDarkLightning12mo ago
In the most respectful way possible, you haven't built complex enough apps if you can't think of use cases for this What do you mean here, you're moving into the client boundary
shikishikichangchang
Usually they would need different information of the org right. I try to select the parts I need for each request. If they’re truly intertwined (one is a child of the other), then I’d just pass them down as props
iDarkLightning
iDarkLightning12mo ago
I don't want to render on the client, the whole point is I do it on the server You cannot pass them down as props from the layout
shikishikichangchang
Not for layouts but normal components I see what you mean
iDarkLightning
iDarkLightning12mo ago
What if the components are siblings
shikishikichangchang
So let’s say I need the org name in both the layout and the not layout component Then id need to fetch twice Is that what you mean
iDarkLightning
iDarkLightning12mo ago
Next recommends you fetch twice, because they will dedupe
shikishikichangchang
Fetch in the parent
iDarkLightning
iDarkLightning12mo ago
What happened to components only having one purpose
jack
jack12mo ago
To put it simply, other frameworks have support for this feature (passing data from layouts down). Next explicitly mentions that they don’t support this, and explains their own solution. If this wasn’t a common, or at the very least not uncommon, pattern, then why is every major meta framework accounting for it?
shikishikichangchang
Wdym each component is still rendering what they should no?
iDarkLightning
iDarkLightning12mo ago
It's rendering what they should but it still has irrelevant logic Regardless okay If you have 7 different components on a page And they all make an individual network request for data for only that component You are making 7 network round trips to display the data on one page
shikishikichangchang
I see Ok so you’re saying instead of 7 network requests, we should be making one
iDarkLightning
iDarkLightning12mo ago
You should be making as few as possible
shikishikichangchang
Right Before I thought you meant that instead of making two identical requests we should be making one. This was confusing because it’s rare that two components would need identical data
iDarkLightning
iDarkLightning12mo ago
It's not Trust me it's not rare at all
shikishikichangchang
So next is supposed to combine all the requests on the same page into one? Ok
iDarkLightning
iDarkLightning12mo ago
Next is supposed to dedupe requests, so all the same requests should dedupe
shikishikichangchang
Ok Do you prefer to use ReactQueryStreamedHydration or initial data ReactQueryStreamedHydration seems more ergonomic but it’s experimental I think
iDarkLightning
iDarkLightning12mo ago
I haven't used react query streamed hydration yet
shikishikichangchang
Alright Thanks for the discussion
iDarkLightning
iDarkLightning12mo ago
no problem
shikishikichangchang
Can you give me an example? I looked at NextJS docs and it’s also talking about user
shikishikichangchang
I see So it’s recommending us to use fetch instead of forwarding props I guess that’s more ergonomic
jack
jack12mo ago
The user example is a very common one yea Think about a page with a sidebar, header, sub header, all in different level layouts that need user data
shikishikichangchang
Makes sense 👍🏻
Want results from more Discord servers?
Add your server