Skip to content

Instantly share code, notes, and snippets.

@adnauseum
Created July 20, 2020 17:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save adnauseum/cbfe9cee8bcc215817af2d445902b9a3 to your computer and use it in GitHub Desktop.
Save adnauseum/cbfe9cee8bcc215817af2d445902b9a3 to your computer and use it in GitHub Desktop.
import { useEffect, useState } from 'react';
import localforage from 'localforage';
const SIX_HOURS_IN_MILLISECONDS = 21600000;
type CacheOptions = {
expireAtMilliseconds?: number;
};
type CachedItem = {
key: string;
value: any;
options: CacheOptions;
};
type HookApi = {
getItemAsync: (key: string) => Promise<any | null>;
setItemAsync: (
key: string,
value: any,
options: CacheOptions
) => Promise<void>;
};
function useBrowserCache(
configuration: CacheOptions | LocalForageOptions
): HookApi {
const [browserCache, setBrowserCache] = useState<LocalForage>(null);
useEffect(() => {
if (browserCache) return;
async function createNewInstanceIfExistingHasExpired() {
// Create a namespaced store in the browser's cache APIs. If the store exists already,
// and hasn't expired, the store is returned. The localforage method might be more aptly
// named: `createOrReturnInstance` ¯\_(ツ)_/¯
const newOrExistingStore = localforage.createInstance({
name: 'useBrowserStorage',
...configuration,
});
const expiration = await newOrExistingStore.getItem('storeExpiration');
if (!expiration) return setBrowserCache(newOrExistingStore);
if (Date.now() < expiration) return setBrowserCache(newOrExistingStore);
await localforage.dropInstance({ name: 'useBrowserStorage' });
const newStore = localforage.createInstance({
name: 'useBrowserStorage',
});
await newStore.setItem(
'storeExpiration',
Date.now() + SIX_HOURS_IN_MILLISECONDS
);
return setBrowserCache(newStore);
}
let current = true;
if (current) createNewInstanceIfExistingHasExpired();
return () => (current = false);
}, [browserCache]);
async function setItemAsync(key: string, value: any, options: CacheOptions) {
try {
// Under the hood, each item is stored as an object: { value: any, options: CachedItemOptions }
// to enable the ability to expire individual items in the cache
await browserCache.setItem(key, {
value: value,
options,
});
} catch (error) {
console.error(error);
return error;
}
}
async function getItemAsync(key: string): Promise<void> {
try {
const cachedItem: CachedItem = await browserCache.getItem(key);
if (!cachedItem) return null;
if (Date.now() >= cachedItem.options.expireAtMilliseconds) {
browserCache.setItem(key, null);
return null;
}
return cachedItem.value;
} catch (error) {
console.error(error);
return error;
}
}
return {
getItemAsync,
setItemAsync,
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment