>> Scott Moss: Now we're gonna talk about caching and memoizing and
how much easier it is.
It's an opt in caching, as you guys know, through the errors that you saw.
You have to decide on how you want to treat dynamic pages.
And by default, Next JS detects dynamic pages for you,
depending on what APIs you access.
So if you access cookies, if you fetch data on a server component,
if you access parameters, if you access short lived caches that Next JS offers
you, if you access search parameters, if you access headers, if I didn't say that
already, there's probably something else if you access any of that stuff.
Next JS, this is a dynamic route, and because it is a dynamic route,
what do you want to do?
Do you want to cache it or do you want to suspend it?
Which one?
Right, so that's where the error comes from.
It won't show you that error on a page that is not dynamic.
So if you just made a regular page with nothing dynamic on it, no data fetching,
just pure JSX, you won't get that error because it's like, this is a static page.
I don't need to.
I know what to do here.
I'm just gonna cache it.
This data never changes, so it just caches the whole page.
So it's only for dynamic pages, which is essentially what people want it.
Because I mean, I should have just took a screenshot of what the docs look like and
put it in here.
What it was like to go through caching before, what you're about to do now.
There was like router cache,
client caching, data caching, memoizing.
It was just so many levels to it and it was just really,
really hard to understand.
So we won't have to do that.
So let's talk about caching.
We've been talking about suspending.
What if we go the other route and we're like, actually,
let's just cache this instead?
What does that look like?
So we'll go through an exercise to see how easy it is.
The first thing we want to do is take advantage of this new directive called
Use Cache.
This is the only directive that is not created by the React team.
This one is actually created by the Next JS team.
It's called Use Cache.
So if you're just using React or if you're using Remix,
this won't exist unless they make it.
But as of right now,
I don't know if they have any plans on making any of this stuff.
This is a Next JS exclusive thing.
It's called Use Cache.
So the way Use Cache works is you can use it inside of a component.
So this is like a page or a layout, and it will cache that entire segment.
So in the case of a layout, if you put it at the top of a layout,
that whole layout will be cached and all of its children.
And then down the leaf you can opt in for
some of those parts to be dynamic if you want.
But that's more of like the same way, whereas suspending the whole page would
block the whole page from loading until something loaded in would not be a good
user experience, caching the whole page might lead to stale data in some places.
So you want to get really granular with your caching for
the same reasons you want to get really granular with what you suspend, right?
So that is the whole point.
So to get around that, you can put Use Cache in a function and
not just a component.
And when I say functions, I'm not talking server actions.
You cannot put Use Cache in server actions,
you cache the functions that you use on the data access layer.
So those functions that you use when a server function is rendering,
that's where you can cache.
So any data fetching function for a React server component, you can use cache.
And that's how you get down to as low as where you wanna be.
So think of suspense is like getting down to the one component that's fetching.
And think about caching as like getting down to the one function that's
getting data.
You wanna get as close as you can to that, in my opinion, and
that just leads to a smoother, more predictable, cleaner experience.
So first thing is you do the use cache directive.
What we're gonna do is let's go ahead and cache the getIssues.
So the getIssues is a function that we use for the dashboard page, right?
Did my JWT expire, I got to sign up again.
Hold up,
.admin@ai.com admin.
Imagine having that domain.
That would be insane.
Absolutely insane.
Okay, so the getIssues is the one that's being called here, right?
So we wanna cache this.
So where I don't see this skeleton every time I refresh.
And I'm not doing a hard refresh, I'm just doing a regular refresh.
I'm doing Cmd+R on my MacBook Air.
So regular refresh, it's still clearly getting the issues over and
over again because that mock delay is running.
So I know it's calling that function every time.
So if I go to my data access layer and I go to getIssues at the top here,
if I put use cache and we save that, now I'm refreshing.
I don't see it anymore.
It's not calling that function anymore.
It only called it that.
I'll do a hard refresh.
There's a heart refresh, then every regular refresh after that.
To do a heart refresh you can do Cmd+Shift+R on a MacBook.
So that's it you get caching just by doing that.
It's really great.
Super free.
This would have been turned on by default in the previous versions of Next JS.
Okay, but now we have a problem.
So what happens if I go create a new issue and I create it,
I go back, it doesn't show up because it's cached.
So you can see why people were pissed.
Because this is what happened by default, right?
Imagine that, you're like, wait, what?
You think your app's broke, right?
You think something's going on.
You're like, why is this not showing up?
And then when you do a Cmd+Shift+R, you're like, there it is.
It's here now.
So people didn't like that.
So this is why they changed it.
So how do we get around that?
Well, we have to revalidate the cache.
We gotta evict it, we gotta expire it.
So there's two things we need to do there.
The first thing is we need a way to reference what we just cached.
The best way to do that is with a cache tag.
So to use a cache tag inside of this function,
we can use this unstable cache tag.
Next JS puts the word unstable in front of everything that isn't out of Canary yet.
So don't be scared that it's unstable.
It just means that it's not out of Canary yet.
They're just taking their time.
It works pretty good.
So unstable cache tag, and you just want to give your cache any tag that you want.
You can make this dynamic.
If you wanna cache with certain properties or IDs or
user ID in this case, this is just always gonna be issues.
So I'm just gonna call it issues, right?
So let's go do that.
Import unstable cache tag.
I'm just gonna refer to it as cash tags.
There's no way I'm writing that out again.
Go down to where our cache is.
Where did I?
Okay, there we go.
And then I can just say, cacheTag issues.
Cool. So that's the first step is tagging it.
The next step is revalidating it after a server action.
So you can't call these functions the cacheTag and
the revalidate tag that I'm about to show you on the client,
they have to happen on the server.
So the only way to revalidate this is inside of a server action.
So it has to be on server.
So to do that I'm gonna go to this actions, this issues actions right here.
These are server actions cuz it has user at the top.
I'm gonna go to where we create an issue right here.
I'm gonna go down to where it is successful right here,
right before we return.
And I want to revalidate the tag called issues.
You don't have to await it, you don't have to do anything.
It's just gonna remove it.
That's it.
You can also do this from an API request.
And I know we haven't talked about API routes yet, but that's also server side,
so this has to be on the server side.
>> Speaker 2: This one's been around for a bit then since it's not unstable.
>> Scott Moss: Revalidate tag has been around for a while,
but the other part has not, yeah.
>> Speaker 2: Which is funny cuz they're kinda two sides of the same coin.
>> Scott Moss: I know, exactly.
>> Speaker 2: You can't do one without the other.
>> Scott Moss: Exactly.
>> Speaker 2: How long does it cache for by default?
>> Scott Moss: By default it's cached forever until you do a hard refresh.
There is another function inside of here.
It's called unstable cache life.
And this thing allows you to like, determine how long you want something
to be cached and you can even use it after you make the call.
So that way, if the life of the cache is depending
on the result of the data, you could just do it here.
So I don't know, there was something on the result that you wanted to do
to change this, what they call cache profiles.
You can put stuff in here.
So I can say something like days, I can say something like weeks.
I can even put the exact amount of seconds and days and things that I want as well.
So it's a lot of control over this.
I think this is why it's still unstable, is they're still figuring this part out.
I think this is the part that they're kinda stuck on.
But we're not gonna get into that because I have a feeling that's actually
gonna change.
>> Speaker 3: So in terms of tagging and the caching and then the invalidation.
This feels pretty TanStack Query ish to me in terms of how that works.
>> Scott Moss: Yeah, it kinda does.
>> Speaker 3: Is there a way to get more granular with your cache and
your cache invalidation?
With TanStack Query, you can give it an array of dependencies essentially
to specifically target the one query to invalidate, for instance,
with the getIssue, like Data Access layer function, right?
>> Scott Moss: Yeah.
>> Speaker 3: I'm assuming whatever's happening with Next or
whatever behind the scenes, that they are caching individually based on the ID or
whatever that's coming through the function to get that.
>> Scott Moss: I mean, I think they're just doing a pretty simple key value store
of just whatever key you put in the cache tag, that's what's gonna be cached in
the object that they're sending to the browser.
>> Speaker 3: Gotcha.
So is it only caching one issue at a time then?
>> Scott Moss: Well, in that case the way it works is the reason it's.
So let's talk about this.
It's a good question.
The reason it's a directive like this is because the compiler,
when they see this directive, they can infer all types of stuff.
They can infer if you had inputs here, they can infer those arguments,
they can infer the result, they can grab all the stuff because this directive
allows them to do a static analysis and observe it at run time.
So a lot of stuff is just done for you automatically.
So it's a little bit of magic happening there.
But if you want to get more granular kinda like how TanStack Query does,
they don't have the mechanism for that yet, if you want to do that.
>> Speaker 3: Sounds like they kinda do it for you though.
>> Scott Moss: Right, they kinda do it for you, so you kinda just.
It's a little magic.
But I think the whole React ecosystem, especially with React 19 and
the new React compiler, which by the way, I haven't talked about, but
you can actually use the new React compiler on Next JS.
It's not enabled by default, where it automatically does the use callback and
the use memoize stuff for you, I think there's memes out there right now because
people are saying, React's a compiler now, and that's a symptom of a bad design.
Now you need a compiler to fix all the bad mistakes that your framework had.
I don't see it that way.
I think it's more of just that is the consequence of having a flexible framework
in which people can pretty much do whatever the hell they want.
But when a lot of those decisions made by developers are causing
really ill performant or really slow websites, slow apps,
then if you're the framework creator, maybe you wanna help solve that.
So you add these tools.
But yeah, it's really good that they can do this.
But yeah, it is happening a lot in the background for you.
>> Speaker 3: Gotcha.
>> Speaker 4: Can you create dynamic hashtags then,
like issue comma, like one or whatever or.
>> Speaker 3: Yeah, issue underscore ID or something?
>> Speaker 4: Yeah, right.
>> Scott Moss: Yeah, yeah, there's nothing stopping you from doing that.
I mean, you can interpolate this and do a thing here if you wanted to do that.
So yep, totally cool.
Great, okay, so now we have that.
So let's try this again.
Now let me just refresh everything with hard refresh.
Go here.
Should see this one.
And there we go.
We see this one.
Yes.
>> Speaker 3: Is it a limitation that revalidate can only happen on
a server action, but not in?
What would be the workaround if we needed to revalidate while fetching?
>> Scott Moss: If you needed to revalidate while fetching, I guess technically you
could because those functions in the data access layer do happen on the server, so
there's nothing stopping you from doing that.
But I would say you probably wanna kinda rethink your architecture if you're
revalidating while fetching, because there's probably better ways to do that,
depending on what you're revalidating, because you have other tools
in your toolbox, like you have prefetching with the links.
Yeah, there's so many other ways to do that.
It's kinda hard to imagine a scenario where I would need to do that.
But yeah, technically it's possible.
As long as it happens on the server, it doesn't matter.
Server action, a call to a data access layer function inside of a react
server component, API routes.
It just has to happen on the server.
>> Speaker 3: How about if some sort of revalidation needs to happen in like
a background process or like a web hook or something like that?
How would you handle that?
>> Scott Moss: Well, a web hook.
Good question. So a web hook would just be a API route.
So there's no difference between a web hook handler and
a regular route handler that you would make.
So it'd be the same thing, same syntax, same everything, background job.
Next JS as a framework has no concept of background jobs.
So I guess the real answer to the question is,
if I'm using some other system, how do I revalidate something?
You would need to make a route,
an API route on your Next JS app whose job is only to revalidate when hit.
So then you can just make a get request post request to that route and
revalidate that thing.
And that way you could access it from any system.
Obviously lock it down if you want to lock it down and have it take some secret or
something.
But that's usually the pattern that I see.
Just make a route whose job is literally just to revalidate a thing.
>> Speaker 3: Have you ever had to cache things across, like,
multiple Next JS instances?
>> Scott Moss: Yeah, I mean, in order to solve that problem,
you would need something on your hosting provider, right.?
So that wouldn't be something that lives.
I mean, you would need an external data source.
Vercel has something called Edge Config that does this really well.
But most people will probably use Redis, cool.
And if I refresh, we still get our cache here.
It only gets invalidated after I create an issue.
So pretty simple.
This to me makes sense.
You just call cache when you want a cache.
You invalidate the cache when you don't want the cache.