Let's say I have a reusable component that, inside a useEffect, loads some data from an API, stores it inside a state which is then used by it in some way.
However, in some pages, this same component is reused in multiple places, making multiple calls for the same data.
So, I would like to have a cache of sorts, where I store the retrieved values and reuse it in other instances if it wasn't fetched too long ago.
I don't really want to store it in a global state like Redux or (partially) Context because this is just a small detail of a reusable component.
The only solution I could come up with was using an external variable as the cache:
import { useEffect, useState } from "react";
let cache = null;
let cacheFetchedTime = null;
export const MyDropdown = () => {
const [data, setData] = useState(null);
useEffect(() => {
const fetchData = async () => {
if (cacheFetchedTime && Date.now() - cacheFetchedTime.getTime() < 5000) {
setData(cache);
return;
}
const fetchedData = await fetchData();
setData(fetchedData);
cache = fetchedData;
cacheFetchedTime = new Date();
};
fetchData();
}, []);
return (
<div>
{/* use `data` somehow */}
</div>
);
};
Is this the best (or only) way of doing this or is there a better alternative?
I decided to keep my cache at the top-level of the file where the component is at.
More specifically, I created an HOC that receives the getter function and returns a function that caches its answer for x period of time:
/* createCachedCall.js */
const createCachedCall = ({ callback, ms = 5000 }) => {
let ongoingAsyncCall = null
let result = null
let lastFetchDate = null
return async () => {
if (ongoingAsyncCall) return ongoingAsyncCall
if (lastFetchDate && Date.now() - lastFetchDate.getTime() <= ms)
return result
ongoingAsyncCall = Promise.resolve(callback())
result = await ongoingAsyncCall
lastFetchDate = new Date()
ongoingAsyncCall = null
return result
}
}
/* MyDropdown.jsx */
const getDataCached = createCachedCall({
callback: getData,
ms: 300000, // 5 minutes
})
export const MyDropdown = () => {
const [data, setData] = useState(null);
useEffect(() => {
const fetchData = async () => {
const data = await getDataCached();
};
fetchData();
}, []);
return (
<div>
{/* use `data` somehow */}
</div>
);
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With