Created
July 1, 2024 07:46
-
-
Save erdesigns-eu/88700bf3e6572f838f211ecfadb12f23 to your computer and use it in GitHub Desktop.
TypeScript ClientStorage Class with LocalStorage and React Integration
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* A callback function that is called when a value changes. | |
* @callback SubscriptionCallback | |
* @param {any} value The new value. | |
* @returns {void} | |
*/ | |
type SubscriptionCallback<T = any> = (value: T) => void; | |
/** | |
* A simple client-side storage class that uses localStorage to store values. | |
* @class ClientStorage | |
*/ | |
class ClientStorage { | |
// A map of key to a set of subscription callbacks. | |
private subscriptions: Record<string, Set<SubscriptionCallback<any>>> = {}; | |
/** | |
* Set a value in localStorage. | |
* @param {string} key The key to store the value under. | |
* @param {any} value The value to store. | |
*/ | |
set<T>(key: string, value: T): void { | |
if (typeof localStorage !== 'undefined') { | |
// Get the type of the value. | |
let type: string = typeof value; | |
// Create a variable to store the value. | |
let storedValue: string | T = value; | |
// Convert the value to a string if it is not a string, boolean, or number. | |
if (value instanceof Date) { | |
type = 'date'; | |
storedValue = value.toISOString(); | |
} else if (typeof value === 'bigint') { | |
type = 'bigint'; | |
storedValue = value.toString(); | |
} else if (typeof value === 'object') { | |
storedValue = JSON.stringify(value); | |
} | |
// Store the value in localStorage. | |
localStorage.setItem(key, storedValue as string); | |
// Store the type of the value in localStorage. | |
localStorage.setItem(`${key}:type`, type); | |
// Call the subscription callbacks for the key if they exist. | |
if (this.subscriptions[key]) { | |
for (const callback of this.subscriptions[key]) { | |
callback(value); | |
} | |
} | |
} | |
} | |
/** | |
* Get a value from localStorage. | |
* @param {string} key The key to get the value for. | |
* @param {T} defaultValue The default value to return if the key does not exist. | |
* @returns {T} The value stored under the key, or the default value if the key does not exist. | |
*/ | |
get<T>(key: string, defaultValue: T): T { | |
if (typeof localStorage !== 'undefined') { | |
// Get the value from localStorage. | |
const value = localStorage.getItem(key); | |
// If the value is null (i.e. the key does not exist), return the default value. | |
if (value === null) { | |
return defaultValue; | |
} | |
// Get the type of the value from localStorage. | |
const type = localStorage.getItem(`${key}:type`); | |
// If the type is null (i.e. the key does not exist), return the value as is (as a string). | |
if (!type) { | |
return value as unknown as T; | |
} | |
// Convert the value to the correct type. | |
switch (type) { | |
case 'boolean': | |
return (value === 'true') as unknown as T; | |
case 'number': | |
return parseFloat(value) as unknown as T; | |
case 'bigint': | |
return BigInt(value) as unknown as T; | |
case 'date': | |
return new Date(value) as unknown as T; | |
case 'object': | |
return JSON.parse(value) as T; | |
default: | |
return value as unknown as T; | |
} | |
} | |
// If localStorage is not available, return the default value. | |
return defaultValue; | |
} | |
/** | |
* Subscribe to changes to a value in localStorage. | |
* @param key The key to subscribe to. | |
* @param callback The callback to call when the value changes. | |
* @returns An object with a stop method that can be called to stop the subscription. | |
*/ | |
subscribe<T>(key: string, callback: SubscriptionCallback<T>): { stop: () => void } { | |
// Create a new set of subscriptions if one does not exist for the key. | |
if (!this.subscriptions[key]) { | |
this.subscriptions[key] = new Set<SubscriptionCallback<any>>(); | |
} | |
// Add the callback to the set of subscriptions. | |
this.subscriptions[key].add(callback); | |
// Return an object with a stop method that can be called to stop the subscription. | |
return { | |
stop: () => { | |
this.subscriptions[key].delete(callback); | |
}, | |
}; | |
} | |
} | |
export default ClientStorage; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Example Using ClientStorage in a React Component with Generics