Client-Side Data Caching
Hilla caches client-side views out of the box, but not client-side data.
This can even lead to a situation where the client-side view is available offline, which isn’t very useful, since there is no data.
To overcome this, you can use localStorage
to store the client-side data, so that the data is available even offline.
Cacheable Wrapper with Local Storage
Assume you have a function to retrieve the data from the backend.
After the data is retrieved, you can save it into localStorage
as a key-value pair.
It’s also good to provide a default value, in case the data is available from neither the backend nor cache.
Example: define a cacheable helper in cacheable.ts
to cache client-side data.
export async function cacheable<T>(fn: () => Promise<T>, key: string, defaultValue: T) {
let result;
try {
// retrieve the data from backend.
result = await fn();
// save the data to localStorage.
localStorage.setItem(key, JSON.stringify(result));
} catch {
// if failed to retrieve the data from backend, try localStorage.
const cached = localStorage.getItem(key);
// use the cached data if available, otherwise the default value.
result = cached ? JSON.parse(cached) : defaultValue;
}
return result;
}
Now you can use the cacheable wrapper in your client-side view.
Example: use the cacheable wrapper.
// import the cacheable function from the path to cacheable.ts file (without the suffix)
import { cacheable } from 'path-to-cacheable';
// instead of always relying on the backend to retrieve the data. e.g.
// const stats = await getStats();
// you can now wrap getStats into the cacheable helper.
const stats = await cacheable(() => getStats(), 'stats', {});
Clear the Local Storage
It’s good practice to clear the data stored in the localStorage
, for example when a user logs out.
Suppose there is a logout
event.
The above cacheable wrapper could be improved as:
// the name of the cache for offline usage
const CACHE_NAME = 'offline-cache';
window.addEventListener('logout', () => {
// clear the data from localStorage when a user logs out
clearCache();
});
export async function cacheable<T>(fn: () => Promise<T>, key: string, defaultValue: T) {
let result;
try {
// retrieve the data from backend.
result = await fn();
// save the data to localStorage.
const cache = getCache();
cache[key] = result;
localStorage.setItem(CACHE_NAME, JSON.stringify(cache));
} catch {
// if failed to retrieve the data from backend, try localStorage.
const cache = getCache();
const cached = cache[key];
// use the cached data if available, otherwise the default value.
result = cached === undefined ? defaultValue : cached;
}
return result;
}
function getCache(): any {
const cache = localStorage.getItem(CACHE_NAME) || '{}';
return JSON.parse(cache);
}
function clearCache() {
localStorage.removeItem(CACHE_NAME);
}