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
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
Wdym by make a request to an API route to invalidate?
You want to invalidate items in Next's data cache?
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
You need to make an HTTP request to the Next server that calls
revalidatePath()
from your client componentWow that seems unnecessarily complicated 🥲
Do you have a good understanding of the 4 types of caches
That's an excellent question
I’m so confused after reading the docs
If you ask questions I can try to answer them
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
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.
And then just call them in componentS
read this first
Ok
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
It's defined because it's in a suspense
Read that as well
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
Yes, pretty much
I'm not sure what you mean
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?
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
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)?
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
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?
The first one is pinned in the trpc discord, the second one was written by Josh, who's fairly active on this server
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
if you don't re-create the client then why would it be broken?
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
you're going to have to be more specific than "the next cache", there's like 4 different caches
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
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
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
im going to have figure this out lol 😭
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
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
I do want to ping @Josh here, interested if you have anything to say on this?
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
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.
Read the readme for more info there
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?
Depending on the change yes
Hahahaha I also have a very "break often fix fast" mindset
But you don't get to choose do you
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
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
lmfao I meant you don't get to choose what changes you use router.refresh for
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
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
Yeah just use a context
I actually do that in my app
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
Also When I'm sober I can help much more lmao
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
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
If you can't use refresh I'd just optimistically update + revalidate
I feel like I'm missing something
Revalidate how
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
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
@iDarkLightning what does @jack mean what he said the fetching is gonna happen twice on the server? Why would it happen twice?
Here
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
I see
What’s a use case that you would need to fetch the same thing on different components (in one tree)?
you fetch the user in the layout to do an auth redirect, you fetch it somewhere else to display user data
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
use session is client side
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
Just anytime you want to render data from the same request in two components
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?
Two different things can rely on data from one fetch
I'm a bit confused this is a very very common thing lol
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
A layout that shows a users profile picture and username, and the page shows other data that depends on user data for example
For example let's say you have a multi tenant app, and you need the org info in several places
This is exactly the pattern that causes this issue
I think I was slightly unclear, it's not just the same data**
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
It makes one network request instead of 30
You can just augment the user object. User is the only use case I can think of
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
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
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
Not for layouts but normal components
I see what you mean
What if the components are siblings
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
Next recommends you fetch twice, because they will dedupe
Fetch in the parent
What happened to components only having one purpose
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?
Wdym each component is still rendering what they should no?
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
I see
Ok so you’re saying instead of 7 network requests, we should be making one
You should be making as few as possible
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
It's not
Trust me it's not rare at all
So next is supposed to combine all the requests on the same page into one?
Ok
Next is supposed to dedupe requests, so all the same requests should dedupe
Ok
Do you prefer to use ReactQueryStreamedHydration or initial data
ReactQueryStreamedHydration seems more ergonomic but it’s experimental I think
I haven't used react query streamed hydration yet
Alright
Thanks for the discussion
no problem
Can you give me an example?
I looked at NextJS docs and it’s also talking about user
I see
So it’s recommending us to use fetch instead of forwarding props
I guess that’s more ergonomic
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
Makes sense 👍🏻