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.
Source code
TypeScript
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.
Source code
TypeScript
// 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:
Source code
TypeScript
// 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); }