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 = Dispatch>; /** * Returns a stateful value from storage, and a function to update it. */ export function useStorage(key: string, initialValue: T, area: StorageArea = 'local'): [T, SetValue] { const [storedValue, setStoredValue] = useState(initialValue); useEffect(() => { readStorage(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>(); 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(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 = 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(key: string, area: StorageArea = 'local'): Promise { 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(key: string, value: T, area: StorageArea = 'local'): Promise { try { await chrome.storage[area].set({ [key]: value }); return true; } catch (error) { console.warn(`Error setting ${area} storage key "${key}":`, error); return false; } }