2004981Plate dynamics lab 2: a hammer on a hung plate by Rayleigh-Ritz and by Mindlin-Reissner finite elementsJeremy Magland 1import { useCallback, useEffect, useState } from "react";
3const PREFIX = "plate-dyn:";
5/** React state mirrored into localStorage under `key`. `validate` rejects stale shapes. */
6export function usePersistentState<T>(
7 key: string,
8 initial: () => T,
9 validate?: (v: unknown) => v is T,
10): [T, (v: T | ((prev: T) => T)) => void, () => void] {
11 const full = PREFIX + key;
12 const [value, setValue] = useState<T>(() => {
13 try {
14 const raw = localStorage.getItem(full);
15 if (raw !== null) {
16 const parsed: unknown = JSON.parse(raw);
17 if (!validate || validate(parsed)) return parsed as T;
18 }
19 } catch {
20 /* fall back to the default */
21 }
22 return initial();
23 });
24 useEffect(() => {
25 try {
26 localStorage.setItem(full, JSON.stringify(value));
27 } catch {
28 /* storage unavailable: state simply does not persist */
29 }
30 }, [full, value]);
31 const reset = useCallback(() => setValue(initial()), [initial]);
32 return [value, setValue, reset];
33}
35/** Forget everything the lab has stored in this browser. */
36export function clearPersistentState(): void {
37 try {
38 const keys: string[] = [];
39 for (let i = 0; i < localStorage.length; i++) {
40 const k = localStorage.key(i);
41 if (k && k.startsWith(PREFIX)) keys.push(k);
42 }
43 keys.forEach((k) => localStorage.removeItem(k));
44 } catch {
45 /* ignore */
46 }
47}