Caching API responses in React
Why do we need to cache API responses in web applications? Some of the data that we get from API doesn’t require updating ten times per session. It might be a list of products available in the store or checking for validation of a street address. Such information might be updated once per day or even more rarely. It’s quite common to place an API request in a React Effect hook. But it will lead to API requests with each render of the component.
Let’s take a look at an example:
const PriceList = ({zipcode}: {zipcode: string}) => {
const [priceList, setPriceList] = useState();
useEffect(() => {
getPriceList(zipcode).then( data => {
setPriceList(data.list);
})
}, [zipcode]);
return <div>{JSON.stringify(priceList)}</div>
}
The implementation looks nice, but how does the getPriceList work inside?
const getPriceList = (zipcode: string): Promise<APIAnswer> => {
const request = `${env.urls.priceListAPI}/${zipcode}/`;
return axios
.get(request)
.then((response) => {
return response.data;
})
.catch(() => {
throw new Error('API error');
});
};
Ook. It means that with each call of the getPriceList we will call the API, and what if we already have checked the zip code once? Can we avoid calling the API? For sure!
Let’s define a wrapper function for caching responses. The function will take care of storing the responses in a Map and will provide an immediate response if we already asked about the price list in this area.
const priceListsCacheMap: Map<string, Promise<APIAnswer>> = new Map();
export const getPriceListByZipcode = (zipcode: string): Promise<APIAnswer> => {
if (!priceListsCacheMap.has(zipcode)) {
priceListsCacheMap.set(zipcode, getPriceList(zipcode));
}
return priceListsCacheMap.get(zipcode);
};
Now we can call getPriceListByZipcode in any place of our application and be sure, that if we already called the function with the same zip code we will get the result immediately without calling a backend API.
priceListCacheMap is defined once in the bundle, and if you refresh the page this store will be reset. It’s not necessary to define it as a separate const, another way is to put this store in your app context. Anyway, this is an example of how to avoid unnecessary API calls.