Next.js Fundamentals, v4

Memoizing

Scott Moss
Superfilter AI
Next.js Fundamentals, v4

Lesson Description

The "Memoizing" Lesson is part of the full, Next.js Fundamentals, v4 course featured in this preview video. Here's what you'd learn in this lesson:

Scott demonstrates how to use the `cache` function from React to memoize a function in a server component in Next.js. He also shows how to observe the memoization in action and mentions that in development mode, Next.js calls components twice, but in production mode, the memoization should work as expected.

Preview
Close

Transcript from the "Memoizing" Lesson

[00:00:00]
>> Scott Moss: Memoizing. So, anybody know what memoizing is? Anybody have a definition for that?
>> Student: It's like caching the answer to a function so that when you call that function with the given input, you already have the answer if you've already called the function with those inputs.
>> Scott Moss: Yeah, it's essentially like I have some computation that might have been expensive.

[00:00:31]
I'm probably gonna run it more than one time. And it's also like a pure function. So if I give it, let's say it's a function that takes two numbers and it returns the sum. If I call that function with the same two numbers, it's always gonna return the same sum no matter what.

[00:00:47]
So that is a great candidate to be memoized because if it gets the same arguments I can expect this. It's a side effect free pure function. So given that, yeah, memoization is really cool because it's a form of caching that speeds things up dramatically. The reason it's super useful in our use case is because we might be in a position sometimes where we have what we call kind of like a waterfall effect of like.

[00:01:13]
Because, now that you know that you can have different data functions being called in different layers when someone refreshes just one route, what if some of those things call the same functions? In the case of like get the current user. What if the sidebar is calling get the current user and the dashboard is calling get current user and the issues list is calling get current user.

[00:01:36]
All three of those load up when you go to our index route. So is it just gonna call that three times? It's gonna hit our database three times. It's gonna access the cookies three times. It's really expensive to be doing that performance wise and maybe even cost. So like you want to avoid that and memoization is a great way to do that.

[00:01:58]
The problem is you don't want that to leak across users. You only want that to happen for that one request. So that way some user doesn't see somebody else's data, right? So the memoization in React and Next JS is really great because it only works for that one request and it's pretty simple to do.

[00:02:19]
So let's give it a try. The way that we do that is we're gonna use this cache function that actually comes from React. This isn't something Next JS made. This is a React thing. It's literally called cache. And its job, it was created for this purpose of caching functions that you call in your React server components.

[00:02:39]
That's exactly what it's made for. It's very reminiscent of if you've ever built a GraphQL server. I don't know if anybody has experience doing that, but when you make resolvers on a GraphQL server, a lot of those resolvers can be cyclic and they might call the same thing, they might do the same thing.

[00:02:55]
So what you do is you make a data loader, which is essentially a memoization of the results of those computations. So even though your GraphQL server is cyclically calling the same things it only ever accesses the cache. It's pretty much the same thing. And it happens for one request and it significantly speeds up your GraphQL responses.

[00:03:13]
So that's what we're gonna do here. So git current user is a great candidate. We call that a lot in many different places. And we want to cache it. So it doesn't do it all the time, but there's a lot of setup and some things you need to understand because once you set this up, it isn't obvious that it's actually working.

[00:03:30]
So I have to show you how to observe it, because Next JS does some funky stuff. So the first thing is, yeah, let's import the cache function and we'll wrap our git current user to use it. So let's do that. And that's gonna be inside of the here.

[00:03:52]
And this, like again, you won't be memoizing server actions because server actions don't happen when a page loads up. Server actions happen on an interaction. You have more control of when that gets called. You don't really need to memoize it. But in the case of components rendering because somebody went to a page, yeah, you do wanna memoize that because there's less control of when those things get called.

[00:04:18]
That really depends on where the components are in the render tree and how they are architect. So these are only gonna happen on functions that get called for server component. So get current user. Here we go. So we have cache. All we have to do is just say cache.

[00:04:35]
That's it. That's all we got to do. The other thing is, right now we don't actually have a scenario. Well, we do, but let's make a perfect scenario so we can observe this. Let's make a scenario in which we actually do for sure call get current user on one request so we can observe this.

[00:04:52]
I think the best place to do this is if you add await get current user on the dashboard page. We will for sure have two because if we go look at the dashboard layout, we're using navigation and then navigation is using user email and then user email is using get current user.

[00:05:09]
So there's one. And then obviously the layout is gonna render the children, which is gonna render this page. And if we put it here, this will be two. So we will for sure. So that means if you go to this page and we add it here, get current user.

[00:05:22]
We don't need to use it. I just wanna show you we will for sure be calling get current user twice. We'll be calling it here and we'll be calling it in that navigation, which is in the layout. So it's a great candidate. So what we can do is let's add a log temu debugger, console log.

[00:05:47]
There we go. So we can say get user. Get current user. Right, so let's do that. If we refresh this, I don't know why I hit refresh in the terminal like that I was going to do something. Refresh this. We'll see get current user do more than what we thought we would see.

[00:06:12]
I don't know why I keep hitting refresh in there. There we go. We'll see it a few times. So I'm gonna talk about this. So we should only see it two times. We only really called it twice, but we see it four times. That is because while you're in development mode Next JS will call your component twice.

[00:06:29]
It always calls the component twice. The reason it does that is to help you detect any cyclic state errors in your components. This is because a lot of people do a use effect and that use effect usually can lead to a lot of issues where it's like changing a state that then changes a render and then it keeps rendering indefinitely.

[00:06:51]
So they run it twice so they can cache that. In development mode, all your stuff is gonna run twice. So that's why we're gonna see four logs here. The goal is just to see one, right? If we do this right, we should only see this one time. So in order to do that one, we got to get out of development mode.

[00:07:06]
And then two, we don't have to do anything else we just did it. So we just need to get out of development mode and start that. And we should be able to see if for sure we'll at least go down to two, because it's not gonna call our components twice anymore in production mode.

[00:07:22]
But we should see it once because we added the cache, if that makes sense. Cool. Okay, so let's do it. Before we can do that, we might have to make some changes to make the production build actually work. At least in my case, because I could care less about TypeScript errors.

[00:07:43]
I have to turn off the TypeScript errors and the ESLint errors, otherwise it won't build locally. So I'm just gonna ignore those for now. So I could actually build this. If you try to build this right now, it might actually freak out. Actually, I'll show you. So I'll just say we actually have a command for this.

[00:08:01]
So if you go to package data, you see there's a command here that says build. So if you run npm run build, it's gonna build it. If it builds successfully, we want to start it, right? So let's try that. Creating optimized production builds. And you can see right here, it failed to compile.

[00:08:26]
And the reason it failed to compile is because TypeScript errors. All right. I could just fix those, but I have my AI turned off, so I'm not gonna fix. Instead I'm just gonna ignore this stuff. And the way I can do that is go to my next JS config can go in here.

[00:08:46]
I can say, hey, for TypeScript, can you just ignore those build errors please? And then also for ESLint, can you ignore those during the build as well? I'm a big fan of TypeScript at dev time in the editor. I don't really like it at build time. It's kind of annoying in my head.

[00:09:07]
I don't care. I just want it to work in my editor. I wanna see the squigglies, but I don't want you yelling at me when I deploy. That's just a preference of mine, I guess. Cool. So now if I run build, you can see it tried to get current user there multiple times.

[00:09:25]
And this is actually really cool. We can show this. This is the output of the build and it's kind of cool how it goes through this. So if you see a circle, a dot, next to a route, that means that that page was statically rendered. There was nothing dynamic about that page, so it was pre rendered.

[00:09:41]
And it's static content, it's never gonna change. It's that way for eternity until you build it again. If it has an f next to it, which is like a function, this is a dynamic page. This page is not statically rendered. It is a server rendered on demand. That means when someone goes to this page, then we will render it.

[00:10:00]
And that is because most likely you're touching the cookies, the headers, fetching data, touching the search params, the route params. You're doing something that tells Next JS that this is a dynamic page and it should not be statically rendered. So it does that for you automatically, which I think is really cool.

[00:10:19]
Okay, cool. So we've built this. Now we can do npm start. And we can see if our cache works. If our cache doesn't work, then I think that's because we got to give it an argument so it can actually cache on the key. But I have a feeling it might work without a cache key if you don't give it anything.

[00:10:44]
Let's check it out. So what we wanna do is just be able to go to the page here, refresh that, and there it is. Get current user one time. Even though we're using it in two places on the same request. So in production it'll work. So don't freak out if you're developing, you're like, why is this cache not working?

[00:11:04]
Development is weird. Just build it in production and try it out there.
>> Student: So I have a question about this, what I'm seeing. So it's not caching it for eternity if you're just using the cache tag from React, right? It's just caching it once per request, right?
>> Scott Moss: Per request.

[00:11:20]
Yeah, this is per request. Yeah.
>> Student: So yeah, because I'm still seeing if you navigate to a new issue, you're using the get current user.
>> Scott Moss: It's gonna call it again.
>> Student: Go back.
>> Scott Moss: Yep.
>> Student: But using the stuff from Next JS, if you were to use the cache tags, it does cache forever.

[00:11:33]
>> Scott Moss: It does cache forever. Yep.
>> Student: Until you revalidate.
>> Scott Moss: Yep.
>> Student: Got it.
>> Scott Moss: They honestly should not call it cache, but I get it. React already has two things called memoize. So they're like, we can't add a third thing called memoize, but technically it-
>> Student: Is just memoizing the actual function.

[00:11:49]
>> Scott Moss: Yeah.
>> Student: Yeah, okay, yeah.
>> Scott Moss: I mean with those three things, caching, cache revalidating, and the memoization, you can make some pretty performant websites pretty easy. You're in control. It makes sense. You can suspend things when you need to, you can cache things when you need to, you can just go static when you need to without really having to think about it.

[00:12:12]
So again, it is different than like what you would mostly see. But like, once you get past that difference, it is a very simple developer experience in my opinion. Big fan of it.
>> Student: I guess more of like a big picture question. So let's say you need a client component that needed access to your user.

[00:12:31]
>> Scott Moss: Okay.
>> Student: Would an approach maybe be like you cache it on the client too?
>> Scott Moss: Yeah, so question was if you have a client component that needs access to your user. The way that I would do that? Well, I guess it depends on if you're using React Query or something like that.

[00:12:48]
Okay, using React Query, you can actually make a server action that gets data. So I would just make a server action that gets the user and then I would pass that server action as the fetcher function to React Query and then React Query will just get the user for it-

[00:13:03]
>> Student: It will cache it on to the client.
>> Scott Moss: And then React Query will cache it on the client and then it's all React Query land at that point.
>> Student: Yeah, that's very cool.
>> Scott Moss: Yeah.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Start a 7-Day Free Trial