Caching is a critical optimization technique to enhance the performance of web applications. By storing API responses locally, we can reduce the number of requests to the server, decrease load times, and improve user experience, especially in applications with heavy data fetching.
In React, caching API responses can be done in several ways, including using localStorage, sessionStorage, IndexedDB, Service Workers, and libraries like React Query and Apollo Client for managing the caching logic automatically.
In this guide, we’ll look at different methods to efficiently cache API responses in a React application.
1. Caching API Responses with LocalStorage
LocalStorage is an easy-to-use browser storage API that allows you to persist key-value pairs across browser sessions. While it can store only strings, it’s suitable for simple caching purposes.
Example: Caching API Responses in LocalStorage
import React, { useState, useEffect } from 'react';
const fetchData = async () => {
const response = await fetch('https://api.example.com/data');
return response.json();
};
const App = () => {
const [data, setData] = useState(null);
useEffect(() => {
// Check if data is available in localStorage
const cachedData = localStorage.getItem('apiData');
if (cachedData) {
setData(JSON.parse(cachedData)); // Parse and set cached data
} else {
// Fetch and cache data if not available
fetchData().then((data) => {
setData(data);
localStorage.setItem('apiData', JSON.stringify(data)); // Cache response
});
}
}, []);
if (!data) return <div>Loading...</div>;
return (
<div>
<h1>API Data:</h1>
<pre>{JSON.stringify(data, null, 2)}</pre>
</div>
);
};
export default App;
Explanation:
- On component mount, we first check if the API response is cached in localStorage.
- If the data exists in the cache, we use it; if not, we fetch the data from the API and cache it in localStorage for subsequent visits.
- This reduces the need for repeated API requests, thus improving performance.
2. Caching API Responses with SessionStorage
SessionStorage is similar to localStorage, but it only persists data for the duration of the page session (until the browser tab is closed). This can be useful if you want to cache data temporarily within a session.
Example: Caching API Responses in SessionStorage
import React, { useState, useEffect } from 'react';
const fetchData = async () => {
const response = await fetch('https://api.example.com/data');
return response.json();
};
const App = () => {
const [data, setData] = useState(null);
useEffect(() => {
// Check if data is available in sessionStorage
const cachedData = sessionStorage.getItem('apiData');
if (cachedData) {
setData(JSON.parse(cachedData)); // Parse and set cached data
} else {
// Fetch and cache data if not available
fetchData().then((data) => {
setData(data);
sessionStorage.setItem('apiData', JSON.stringify(data)); // Cache response
});
}
}, []);
if (!data) return <div>Loading...</div>;
return (
<div>
<h1>API Data:</h1>
<pre>{JSON.stringify(data, null, 2)}</pre>
</div>
);
};
export default App;
Explanation:
- SessionStorage is used here to cache data for the duration of the session.
- Once the page session ends, the cached data will be lost, which is suitable for temporary data caching (e.g., during form steps or session-based data).
3. Caching with Service Workers (For Offline Support)
A Service Worker is a script that runs in the background, independent of the web page, and can intercept network requests. It can be used to cache API responses and serve them when the user is offline, making your app fully functional offline.
Service Worker Example (with Cache API)
// service-worker.js
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open('api-cache').then((cache) => {
return cache.addAll(['/', '/index.html']);
})
);
});
self.addEventListener('fetch', (event) => {
if (event.request.url.includes('https://api.example.com/data')) {
event.respondWith(
caches.match(event.request).then((cachedResponse) => {
if (cachedResponse) {
return cachedResponse; // Return cached response if exists
}
return fetch(event.request).then((response) => {
// Cache the response for future use
caches.open('api-cache').then((cache) => {
cache.put(event.request, response.clone());
});
return response;
});
})
);
}
});
Explanation:
- In this example, a service worker intercepts the network request for API data.
- If the request has been made before, it returns the cached response from the Cache API; otherwise, it fetches the data from the network, caches it, and returns the response.
4. Using React Query for Caching API Responses
React Query is a powerful data-fetching and state management library that makes caching API responses easy. It automatically caches API responses and provides built-in mechanisms for background data refreshing, refetching, and cache invalidation.
Example: Caching API Responses with React Query
import React from 'react';
import { useQuery } from 'react-query';
const fetchData = async () => {
const response = await fetch('https://api.example.com/data');
return response.json();
};
const App = () => {
const { data, isLoading, error } = useQuery('apiData', fetchData, {
staleTime: 1000 * 60 * 5, // Cache for 5 minutes
cacheTime: 1000 * 60 * 60, // Keep data in cache for 1 hour
});
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<div>
<h1>API Data:</h1>
<pre>{JSON.stringify(data, null, 2)}</pre>
</div>
);
};
export default App;
Explanation:
- React Query automatically handles caching and background refetching.
staleTime
defines how long the cached data is considered fresh, whilecacheTime
controls how long the data stays in the cache after it becomes stale.- This reduces the need for manual caching logic and improves developer productivity.
5. Caching with Apollo Client (for GraphQL APIs)
If you’re working with GraphQL, Apollo Client provides built-in caching for GraphQL queries and mutations, making it a seamless option for caching API responses in React.
Example: Caching API Responses with Apollo Client
import React from 'react';
import { useQuery, gql } from '@apollo/client';
const GET_DATA = gql`
query GetData {
data {
id
name
}
}
`;
const App = () => {
const { loading, error, data } = useQuery(GET_DATA);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<div>
<h1>API Data:</h1>
<pre>{JSON.stringify(data, null, 2)}</pre>
</div>
);
};
export default App;
Explanation:
- Apollo Client caches responses from the GraphQL API automatically, so subsequent requests for the same query will return the cached response (if the cache is valid).
- You can configure cache policies and expiration strategies with Apollo Client for more control over caching behavior.