Client-side data fetching recap
Get the project source code below, and follow along with the lesson material.
Download Project Source CodeTo set up the project on your local machine, please follow the directions provided in the README.md
file. If you run into any issues with running the project source code, then feel free to reach out to the author in the course's Discord channel.
This lesson preview is part of the Blazing Fast Next.js with React Server Components course and can be unlocked immediately with a \newline Pro subscription or a single-time purchase. Already have access to this course? Log in here.
Get unlimited access to Blazing Fast Next.js with React Server Components, plus 70+ \newline books, guides and courses with the \newline Pro subscription.
[00:00 - 00:12] In previous lessons, we have tried to keep static rendering around as much as possible, because that's the fastest and cheapest way to render content. It scales almost infinitely and you can get always fresh data thanks to revalid ation.
[00:13 - 00:23] Now it doesn't mean that we shouldn't use the other rendering methods. Better, we could mix static and client-side or dynamic rendering for a better user experience.
[00:24 - 00:50] This module is specifically dedicated to client-side plus static rendering, and in next modules we are going to discover how to mix static and dynamic server-side rendering. I'm going to show you an interesting pattern that I call "relaying" like in a sport competition when you have a relay between two runners. One runner being static rendering and the other being client-side rendering. Let me run an experiment that will show why it's necessary.
[00:51 - 01:06] I'm going to preorder a product. Remember that this page is static but uses re validation, so I immediately see that I have nine customers who preordered. The ninth being myself. The problem is that I've opened this page in another tab of my browser.
[01:07 - 01:31] Let's see what happened. It is still displaying eight customers. The problem is that server-side rendering needs refresh to be updated, because the page is rendered on the server. The data are obtained at the moment I send an HTTP request, which happens when I open the URL. So if I eat the refresh button, I will get fresh data, and I can check that out in the network development tools.
[01:32 - 04:09] See, I'm running the request again. I check the first request, which is always the server-side render, that's the request that is triggered by your browser when you open a URL automatically. I check the content. And I see my nine customers, so this has been statically rendered with revalidation as we have done in the previous module. No, for this kind of data, refresh is acceptable. But think of other use cases like having a real-time dashboard or real-time chat application. You would want the data to update automatically without any user action. To be able to get fresh data automatically, without having the user to refresh the page, we need client-side JavaScript. To fetch the data and to update the page without a new request and response from the server. Even though static costs nothing in terms of rendering, it is still static. So at this point, you might think, "Okay, but you say that static was very performant, that it was the best way in the world to render content." That's still true. But we can mix static rendering and client-side rendering. Before that, let's focus on how we could implement the client-side rendering without relaying. First, let's refresh our memory, but how we fetch data in React. If you are a React developer, a Next.js developer, you might think, "Yeah, you already know how to get that client-side. The new thing is the server-side data fetching that's the complicated part that I want to learn. That's why I've joined this course." Yet, there has been a new release of React documentation that might change your mind. There are two pages that I find very interesting, the "useEffect" API documentation and "You might not need an effect" documentation. In particular , this documentation shows a reusable hook that I find very interesting that is named "useData." It is using the usual combo of useEffect, useState, and fetch to run asynchronous requests towards the server. Keep in mind that in client-side React, we are still not allowed to have asynchronous components. Only React Server Component can do direct access to the database or direct fetch calls. Client-side components still have to use the hooks to do that. But the thing that we all forget to do, let's be honest, is to clean up the effect correctly. So this hook shows how you can ignore the fetch result if the component that I've been running this hook is unmounted. So I really encourage you to take a look at this hook and use it in your application. You'll find it very interesting.
[04:10 - 04:43] But there's another thing that the documentation mentions. It mentions that instead of writing your own hooks, you should probably prefer using a reusable library. It is stated exactly here. "You can use an effect to fetch data for your component. But not that if you use a framework, using your frameworks data-fetching mechanism will be a lot more efficient than writing effects manually". Below in the deep dive section, it also gives a few arguments about why you should prefer your framework data-fetching mechanism. For instance, effects don't run on the server.
[04:44 - 05:23] So with React Server Components, we need other mechanisms. Anyway, hopefully in React Server Components, it's pretty easy. You just have to call your database literally or call your API, your internal API directly using async/await syntax. That's just pure JavaScript. So very intuitive in Next.js. But even if you stick to client-side data-fetching, you may have issues with caching. Because if you just use a hook this way, the problem is that if you fetch the same data in multiple components, you won't have much reusability. So you will start to involve React Context, that's also possible to share data between multiple components. But it becomes complicated.
[05:24 - 05:37] Then you need logic to invalidate the data. Basically, you end up creating your own data-fetching library. As complex as the ones that already exists that are battle-tested, safe to use, performance.
[05:38 - 06:24] So maybe it's better to use a library. Next.js doesn't bring a specific client -side data-fetching library. The choice is up to you. But what I really like is SWR. SWR is also maintained by Vercel, and it has a few nice features. It's light and simple. It can update data in real time, in quasi-real time. What we call polling. We're going to talk about that in the next lesson. It's compatible with the latest React features, like Suspense. It can handle pagination. It's agnostic to your backend. It works well with server-side rendering, static rendering. It uses TypeScript. Basically, it's a good library. I really like it. So it's a good fit for our Next JS application. Let's take a look at the code. So you can see a lot of explanation in comments.
[06:25 - 06:50] That's what we are going to check in the next lesson. But the actual code to SW R is very simple. You have this hook, useSWR. You have the typing for this hook. We are just telling SWR that the data we receive is a count. It's a JSON object containing a count field. We pass the URL to the API as the first parameter. We pass a fetcher function as the second parameter.
[06:51 - 10:15] A fetcher function is just the function that does the actual fetch call. The idea is that, depending on the context, you might need different fetchers. Maybe you are using GraphQL. It's totally possible to use SWR with GraphQL. The caching mechanism will be a bit weird because SWR caches based on the URL and in GraphQL you have a new one URL. For most APIs, you will want to use a fetch call and expect JSON data. And you have options. And those options will be very useful in the next lesson. But for now, we are just firing the call. At the moment, my components is still using the initial count that has been computed during the static rendering or during the revalidation of the static rendering. We can see here in the page that we use our rscCountPreorders function to get the count of preorders. Notice the RSC prefix, which means that this function is safe to use in a React Server Component. It gets the count and just passes it to my preorder section. Now, SWR is sending me data that it gets from an API route. Note that if you use client-side data fetching, you always need an additional API route to get the data. This API route is using the same function as our rscCountPreorders. If I check the implementation, we can see that it is caching the countPreorders function and our API route is using the countPreorders function. So they are using the same logic, one in a React Server Component, one in an API route that then can be later called by the client-side code. So let's replace our statically-rendered initial count with the data we got from the client. I need to rebuild the application because remember that when testing static rendering in development mode, you might not be able to see what happens because the page will be renderered on every new request like its dynamic. It's because static can be annoying for the developer experience because it's static so you don't see updates. So it's better to test static rendering with the built application. Now, let's run the same experimentation again. Let's preorder the product, open the other tab and I see 12 customers already. That's because SWR will trigger a new request when I open the tab to get fresh data. We can see it in the network. If I try again, but this time with the network development tools opened, I see a new request. SWR has been sending a new request because I've focused the page again. I've opened this tab again on my computer. That's a good reason to use a library because it handles complex interactions like focusing on different tabs in the browser automatically. Now, it is still not perfect because if I keep the tab focused and other people in the world are pre-ordering the product, I don't have an update. I got a fresh value only because I clicked on the tab. Yes, that's a slight progress compared to having to refresh the page, but still not what we want. So in the next session, we are going to discover polling to get fresh data automatically in virtually real time and also using fallback data to have a true static and client relay, because here we are using the client side rendered value, but we could use the static value for maximum performance. If I check the network and the server side rendered version, I will see only one customer because I'm not using my initial count from the server anymore.
[10:16 - 10:17] I need to set up relaying.