Data Fetching with CSR and SSR

Bumgu

2024/01/26

Tags: CSR SSR next.js

While developing, you’ll encounter both CSR and SSR. In this post, I’ll show how to use these two approaches in Next.js to make a GET request and receive JSON data.

1. The difference between CSR and SSR

CSR (Client-Side Rendering)

SSR (Server-Side Rendering)

Summary

CSR: Like placing furniture (data) in an empty room (browser) yourself; extra work is needed to fetch the furniture (data). SSR: An already furnished room (with data) is provided, so the initial load doesn’t show an empty screen.

When should you use each?

SSR
CSR

2. Create an app

First, let’s create a Next.js app. yarn create next-app next-data-fetching

Since we’re only going to fetch data, I didn’t include ESLint or TailwindCSS.

Move into the project with cd next-data-fetching. Run it with yarn dev. This is the default screen of a Next.js app.

3. Data fetching - CSR

URL for the GET request: https://nomad-movies.nomadcoders.workers.dev/movies

Delete all the contents of app/page.tsx and import useState and useEffect.

import { useState, useEffect } from 'react';

export default function Home() {
  const [movies, setMovies] = useState([]);
  return (
  <div>
  </div>
  );
}

3-1. “use client”

Next.js apps use Server Components by default, so you can’t use useState or useEffect. To declare this as a client component, write "use client" at the very top.

"use client"
import { useState, useEffect } from 'react';

export default function Home() {
  const [movies, setMovies] = useState([]);
  return (
  <div>
  </div>
  );
}

3-2. getMovies()

Next, we’ll use fetch to retrieve the data. Create a function called getMovies().

const getMovies = async () => {
	const response = await fetch("https://nomad-movies.nomadcoders.workers.dev/movies");
    const json = await response.json();
    setMovies(json);
    return json
}

getMovies() is an asynchronous function. It sends a request to the specified URL, converts the response to JSON, and sets movies through setMovies(). It then returns the JSON.

3-3. useEffect()

useEffect(() => {
	getMovies();
}, [])

We want getMovies() to run only once on the initial load, so we call it inside useEffect() and leave the dependency array empty.

The data is ready, so let’s display it on the screen.

return (
<div>
{JSON.stringify(movies)}
</div
)

Since this is JSON data, we use stringify() to convert it to a string.

"use client"
import { useState, useEffect } from 'react';

export default function Home() {
  const [movies, setMovies] = useState([]);

  const getMovies = async () => {
    const response = await fetch("https://nomad-movies.nomadcoders.workers.dev/movies");
    const json = await response.json();
    setMovies(json)
    return json;
  }

  useEffect(() => {
    getMovies();
  }, [])
  return (
  <div>
     {JSON.stringify(movies)}
    </div>
  );
}

This is what the final code looks like.

The fetched data is displayed correctly. But as explained above, CSR fetches the data after the initial load. Does it really take time to retrieve the data? Let’s check.

3-4. Add isLoading

While the data is loading, we’ll display Loading...; once all the data has been retrieved, we’ll display the data. First, create isLoading and setIsLoading: const [isLoading, setIsLoading] = useState(true);

Then, below setMovies(json) in getMovies(), add setIsLoading(false). Use a ternary operator in the return statement to render the result: {isLoading ? 'Loading.....' : JSON.stringify(movies)}

"use client"
import { useState, useEffect } from 'react';

export default function Home() {
  const [movies, setMovies] = useState([]);
  const [isLoading, setIsLoading] = useState(true);

  const getMovies = async () => {
    const response = await fetch("https://nomad-movies.nomadcoders.workers.dev/movies");
    const json = await response.json();
    setMovies(json);
    setIsLoading(false);
    return json;
  }

  useEffect(() => {
    getMovies();
  }, [])
  return (
  <div>
     {isLoading ? 'Loading.....' : JSON.stringify(movies)}
    </div>
  );
}

Once data fetching is complete, IsLoading becomes false and movies is rendered. Before fetching finishes, the text Loading..... is rendered.

Since this isn’t a large amount of data, it doesn’t take long, but there is a brief loading period because the data is fetched after the page appears.

4. Data fetching - SSR

Now let’s fetch data using SSR.

Restore the code to its initial state.

export default function Home() {
  return (
  <div>
      {JSON.stringify(movies)}
    </div>
  );
}

I left JSON.stringify(movies) as is because we’ll still use it.

4-1. getMovies()

As with CSR, write the asynchronous getMovies() function.

const getMovies = async () => {
  const response = await fetch("https://nomad-movies.nomadcoders.workers.dev/movies");
  const json = await response.json();
  return json;
}

The data-fetching code itself is no different from the CSR approach. The difference is how it is rendered.

4-2. Render the data

export default async function Home() {
  const movies = await getMovies();
  return (
  <div>
      {JSON.stringify(movies)}
    </div>
  );
}

Change the Home component into an asynchronous function, then assign the return value of getMovies(); to a variable named movies. Since getMovies() is asynchronous, it needs the await keyword.

Final code:

const getMovies = async () => {
  const response = await fetch("https://nomad-movies.nomadcoders.workers.dev/movies");
  const json = await response.json();
  return json;
}
export default async function Home() {
  const movies = await getMovies();
  return (
  <div>
      {JSON.stringify(movies)}
    </div>
  );
}

5. Comparing the results

CSR fetches and displays data after the initial load, so there is a brief loading period. SSR, on the other hand, caches the data ahead of time, so it displays it right away without waiting.

However, SSR is not a magic solution that is always better, so use each approach according to the situation.

That’s all for this post. Thank you.

>> Home