How to Memoize Client-Side Fetched Data in Next.js Using the App Router

Sh Raj - Jun 27 - - Dev Community

How to Memoize Client-Side Fetched Data in Next.js Using the App Router

With the release of Next.js 13, the App Router introduces a new way to define routes and manage data fetching in a more flexible and powerful manner. In this guide, we will demonstrate how to memoize client-side fetched data using the App Router.

Why Memoize Client-Side Fetched Data?

  • Performance Improvement: Reduce redundant data fetching, leading to faster page loads and smoother interactions.
  • Reduced Network Usage: Minimize the number of network requests, saving bandwidth and reducing server load.
  • Enhanced User Experience: Provide instant data access from the cache, leading to a more responsive application.

Implementing Memoization in Next.js with the App Router

Here is a step-by-step guide to memoizing client-side fetched data in a Next.js application using React's built-in hooks and caching mechanisms within the new App Router paradigm.

Step 1: Set Up Your Next.js Project

If you haven't already, create a new Next.js project:

npx create-next-app@latest memoize-example
cd memoize-example
Enter fullscreen mode Exit fullscreen mode
Step 2: Install Required Packages

You may want to use a caching library like lru-cache to help manage your cache. Install it using npm:

npm install lru-cache
Enter fullscreen mode Exit fullscreen mode
Step 3: Create a Cache

Create a cache instance using lru-cache. This cache will store the fetched data.

// lib/cache.js
import LRU from 'lru-cache';

const options = {
  max: 100, // Maximum number of items in the cache
  maxAge: 1000 * 60 * 5, // Items expire after 5 minutes
};

const cache = new LRU(options);

export default cache;
Enter fullscreen mode Exit fullscreen mode
Step 4: Fetch Data with Memoization

Create a custom hook to fetch and memoize data. This hook will check the cache before making a network request.

// hooks/useFetchWithMemo.js
import { useState, useEffect } from 'react';
import cache from '../lib/cache';

const useFetchWithMemo = (url) => {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    const fetchData = async () => {
      setLoading(true);
      setError(null);

      // Check if the data is in the cache
      const cachedData = cache.get(url);
      if (cachedData) {
        setData(cachedData);
        setLoading(false);
        return;
      }

      try {
        const response = await fetch(url);
        const result = await response.json();

        // Store the fetched data in the cache
        cache.set(url, result);
        setData(result);
      } catch (err) {
        setError(err);
      } finally {
        setLoading(false);
      }
    };

    fetchData();
  }, [url]);

  return { data, loading, error };
};

export default useFetchWithMemo;
Enter fullscreen mode Exit fullscreen mode
Step 5: Define a Route Using the App Router

In the new App Router, you define routes using a file-based routing system inside the app directory. Create a new file for your route.

// app/page.js
import useFetchWithMemo from '../hooks/useFetchWithMemo';

const HomePage = () => {
  const { data, loading, error } = useFetchWithMemo('https://api.example.com/data');

  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error: {error.message}</p>;

  return (
    <div>
      <h1>Data</h1>
      <pre>{JSON.stringify(data, null, 2)}</pre>
    </div>
  );
};

export default HomePage;
Enter fullscreen mode Exit fullscreen mode
Step 6: Configure Caching Strategy (Optional)

Depending on your use case, you might want to fine-tune the caching strategy. For example, you can adjust the cache expiration time, the maximum number of items, or even implement cache invalidation logic based on your application's requirements.

// lib/cache.js
const options = {
  max: 50, // Maximum number of items in the cache
  maxAge: 1000 * 60 * 10, // Items expire after 10 minutes
};

const cache = new LRU(options);

export default cache;
Enter fullscreen mode Exit fullscreen mode

Conclusion

By memoizing client-side fetched data in a Next.js application using the App Router, you can significantly enhance performance, reduce network usage, and improve the overall user experience. With the help of caching libraries like lru-cache and custom hooks, implementing memoization becomes straightforward and manageable. Experiment with different caching strategies to find the best fit for your application's needs, and enjoy the benefits of a more efficient and responsive Next.js application.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .