Skip to content

Instantly share code, notes, and snippets.

@0-vortex
Forked from JohnBra/useStorage.tsx
Created February 27, 2024 07:12
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 0-vortex/1b53de29562332eb4e542ccd5d15a9de to your computer and use it in GitHub Desktop.
Save 0-vortex/1b53de29562332eb4e542ccd5d15a9de to your computer and use it in GitHub Desktop.
Typed React useStorage hook for chrome extensions
import { Dispatch, SetStateAction, useState, useEffect, useCallback, useRef } from 'react';
export type StorageArea = 'sync' | 'local';
// custom hook to set chrome local/sync storage
// should also set a listener on this specific key
type SetValue<T> = Dispatch<SetStateAction<T>>;
/**
* Returns a stateful value from storage, and a function to update it.
*/
export function useStorage<T>(key: string, initialValue: T, area: StorageArea = 'local'): [T, SetValue<T>] {
const [storedValue, setStoredValue] = useState<T>(initialValue);
useEffect(() => {
readStorage<T>(key, area).then(res => {
if (res) setStoredValue(res);
});
chrome.storage.onChanged.addListener((changes, namespace) => {
if (namespace === area && changes.hasOwnProperty(key)) {
if (changes[key].newValue) setStoredValue(changes[key].newValue);
}
});
}, []);
const setValueRef = useRef<SetValue<T>>();
setValueRef.current = value => {
// Allow value to be a function, so we have the same API as useState
const newValue = value instanceof Function ? value(storedValue) : value;
// Save to storage
setStoredValue(prevState => {
setStorage<T>(key, newValue, area)
.then((success) => {
if (!success) setStoredValue(prevState);
});
return newValue;
});
}
// Return a wrapped version of useState's setter function that ...
// ... persists the new value to storage.
const setValue: SetValue<T> = useCallback(
value => setValueRef.current?.(value),
[],
)
return [storedValue, setValue];
}
/**
* Retrieves value from chrome storage area
*
* @param key
* @param area - defaults to local
*/
export async function readStorage<T>(key: string, area: StorageArea = 'local'): Promise<T | undefined> {
try {
const result = await chrome.storage[area].get(key);
return result?.[key];
} catch (error) {
console.warn(`Error reading ${area} storage key "${key}":`, error);
return undefined;
}
}
/**
* Sets object in chrome storage area
*
* @param key
* @param value - value to be saved
* @param area - defaults to local
*/
export async function setStorage<T>(key: string, value: T, area: StorageArea = 'local'): Promise<boolean> {
try {
await chrome.storage[area].set({ [key]: value });
return true;
} catch (error) {
console.warn(`Error setting ${area} storage key "${key}":`, error);
return false;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment