| name | client-localstorage-persistence |
| description | Use quando precisar persistir state de React/SPA em localStorage com fallback automático de truncamento (FIFO em listas) quando exceder o limite de 5MB do navegador. |
| allowed-tools | Read, Write, Edit |
client-localstorage-persistence
Helper genérico tipado para persistir state em localStorage com:
- Carregamento seguro em try/catch (lida com
localStorage indisponível em modo privado/SSR).
- Truncamento opcional via callback quando o JSON serializado excede limite configurável (default 4MB para deixar margem em relação ao limite usual de 5MB).
API
type StorageOptions<T> = {
maxBytes?: number;
truncate?: (state: T) => T;
};
function createLocalStorageStore<T>(
key: string,
opts?: StorageOptions<T>
): {
load: () => Partial<T>;
save: (state: T) => void;
clear: () => void;
};
Source
const DEFAULT_MAX_BYTES = 4_000_000;
export function createLocalStorageStore<T>(
key: string,
opts: { maxBytes?: number; truncate?: (state: T) => T } = {},
) {
const maxBytes = opts.maxBytes ?? DEFAULT_MAX_BYTES;
function load(): Partial<T> {
try {
if (typeof localStorage === 'undefined') return {};
const raw = localStorage.getItem(key);
if (!raw) return {};
return JSON.parse(raw) as Partial<T>;
} catch {
return {};
}
}
function save(state: T): void {
try {
if (typeof localStorage === 'undefined') return;
let toSave: T = state;
let serialized = JSON.stringify(toSave);
if (serialized.length > maxBytes && opts.truncate) {
toSave = opts.truncate(toSave);
serialized = JSON.stringify(toSave);
}
localStorage.setItem(key, serialized);
} catch {
}
}
function clear(): void {
try {
if (typeof localStorage === 'undefined') return;
localStorage.removeItem(key);
} catch { }
}
return { load, save, clear };
}
Uso em React
import { useState, useEffect } from 'react';
import { createLocalStorageStore } from './client-localstorage-persistence';
interface AppState {
messages: Array<{ id: number; text: string }>;
channels: string[];
}
const store = createLocalStorageStore<AppState>('myapp:v1', {
maxBytes: 4_000_000,
truncate: (s) => ({ ...s, messages: s.messages.slice(-100) }),
});
function App() {
const persisted = store.load();
const [messages, setMessages] = useState(persisted.messages ?? []);
const [channels, setChannels] = useState(persisted.channels ?? []);
useEffect(() => {
store.save({ messages, channels });
}, [messages, channels]);
}
Adaptation hints
Origin
Extraída de src/context/ChatContext.tsx, funções loadPersisted e persistState (linhas ~80-103). Refactor leve para tornar genérico em <T>.