kcg-monitoring/frontend/src/hooks/useLocalStorage.ts

69 lines
2.2 KiB
TypeScript

import { useState, useCallback } from 'react';
const PREFIX = 'kcg.';
/**
* localStorage 연동 useState — JSON 직렬화/역직렬화 자동 처리.
* 새 키가 추가된 Record 타입은 defaults와 자동 머지.
*/
export function useLocalStorage<T>(key: string, defaults: T): [T, (v: T | ((prev: T) => T)) => void] {
const storageKey = PREFIX + key;
const [value, setValueRaw] = useState<T>(() => {
try {
const raw = localStorage.getItem(storageKey);
if (raw === null) return defaults;
const parsed = JSON.parse(raw) as T;
// Record 타입이면 defaults에 있는 키가 저장값에 없을 때 머지
if (defaults !== null && typeof defaults === 'object' && !Array.isArray(defaults)) {
return { ...defaults, ...parsed };
}
return parsed;
} catch {
return defaults;
}
});
const setValue = useCallback((updater: T | ((prev: T) => T)) => {
setValueRaw(prev => {
const next = typeof updater === 'function' ? (updater as (prev: T) => T)(prev) : updater;
try {
localStorage.setItem(storageKey, JSON.stringify(next));
} catch { /* quota exceeded — 무시 */ }
return next;
});
}, [storageKey]);
return [value, setValue];
}
/**
* Set<string>용 localStorage 연동 — 내부적으로 Array로 직렬화.
*/
export function useLocalStorageSet(key: string, defaults: Set<string>): [Set<string>, (v: Set<string> | ((prev: Set<string>) => Set<string>)) => void] {
const storageKey = PREFIX + key;
const [value, setValueRaw] = useState<Set<string>>(() => {
try {
const raw = localStorage.getItem(storageKey);
if (raw === null) return defaults;
const arr = JSON.parse(raw);
return Array.isArray(arr) ? new Set(arr) : defaults;
} catch {
return defaults;
}
});
const setValue = useCallback((updater: Set<string> | ((prev: Set<string>) => Set<string>)) => {
setValueRaw(prev => {
const next = typeof updater === 'function' ? updater(prev) : updater;
try {
localStorage.setItem(storageKey, JSON.stringify(Array.from(next)));
} catch { /* quota exceeded */ }
return next;
});
}, [storageKey]);
return [value, setValue];
}