useLocalForage

A useState like hook that persists its value through localForage in either local storage or indexedDB.

It has a built in event listener that will ensure the value is updated in the DOM when the value changes. So no need to worry about using shared contexts. Update the value in one place, update it anywhere.

Usage

1 2 3 4 5 6 7 import useLocalForage from "@/hooks/use-local-forage"; export default function Example() { const [value, setValue] = useLocalForage("key", "value"); return <div>{value}</div>; }

Installation

npx shadcn add "https://registry.niels.foo/use-local-forage.json"

Source

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 import localForage from "localforage"; import { useCallback, useEffect, useState } from "react"; localForage.config({ name: "foo", }); /** * A custom hook that provides persistent state management using localForage. * * This hook allows you to store and retrieve data in the browser's local storage or IndexedDB, * providing a persistent state that survives page reloads and browser restarts. * * ```tsx * import useLocalForage from "@/hooks/use-local-forage"; * * export default function Example() { * const [value, setValue, isInitialized] = useLocalForage("myKey", "initialValue"); * * if (!isInitialized) { * return <div>Loading...</div>; * } * * return ( * <div> * <p>Current value: {value}</p> * <button onClick={() => setValue("New value")}>Update value</button> * </div> * ); * } * ``` */ export default function useLocalForage<T>( key: string, initialValue: T ): [T, (value: T) => void, boolean] { const event = `event:${key}`; const [isInitialized, setIsInitialized] = useState(false); const [storedValue, setStoredValue] = useState(initialValue); const retrieve = useCallback(async () => { try { const value: T | null = await localForage.getItem(key); setStoredValue(value == null ? initialValue : value); } catch (err) { console.error(err); } }, [initialValue, setStoredValue, key]); const store = useCallback( async (value: T) => { try { setStoredValue(value); await localForage.setItem(key, value); } catch (err) { console.error(err); } finally { window.dispatchEvent(new Event(event)); } }, [key, event, setStoredValue] ); useEffect(() => { if (isInitialized) { return; } retrieve(); setIsInitialized(true); }, [isInitialized, retrieve]); useEffect(() => { window.addEventListener(event, retrieve); return () => { window.removeEventListener(event, retrieve); }; }, [event, retrieve]); return [storedValue, store, isInitialized]; }