UNPKG

@larscom/ngrx-signals-storage

Version:

Save signal state (@ngrx/signals) to localstorage/sessionstorage and restore the state on page load (with SSR support).

135 lines (129 loc) 3.85 kB
import { effect } from '@angular/core'; import { getState, patchState } from '@ngrx/signals'; class NoopStorage { constructor() { this.length = -1; this.clear = () => { }; this.getItem = (_key) => null; this.key = (_index) => null; this.removeItem = (_key) => { }; this.setItem = (_key, _value) => { }; } } function isBrowser() { return typeof window !== 'undefined'; } /** * Helper function for SSR (Server Side Rendered) apps to get a storage location. * * @example * export const CounterStore = signalStore( * withState({ * count: 0 * }), * withStorage('myKey', getStorage('sessionStorage')) * ) * * Check out github for more information. * @see https://github.com/larscom/ngrx-signals-storage */ function getStorage(storageType) { switch (storageType) { case 'localStorage': { return isBrowser() ? window.localStorage : new NoopStorage(); } case 'sessionStorage': { return isBrowser() ? window.sessionStorage : new NoopStorage(); } default: { return new NoopStorage(); } } } const defaultConfig = { excludeKeys: [], serialize: (state) => JSON.stringify(state), deserialize: (state) => JSON.parse(state), saveIf: (state) => true, error: (error) => console.error(error) }; /** * The `withStorage` function that lets you save the state to localstorage/sessionstorage * and rehydrate the state upon page load. * * @param key the key under which the state should be saved into `Storage` * @param storage an implementation of the `Storage` interface, like: `sessionStorage` or `localStorage` * * @example * // for apps *without* SSR (Server Side Rendering) * export const CounterStore = signalStore( * withState({ * count: 0 * }), * withStorage('myKey', sessionStorage) * ) * * @example * // for apps *with* SSR (Server Side Rendering) * export const CounterStore = signalStore( * withState({ * count: 0 * }), * withStorage('myKey', getStorage('sessionStorage')) * ) * * Check out github for more information. * @see https://github.com/larscom/ngrx-signals-storage */ function withStorage(key, storage, config) { const cfg = { ...defaultConfig, ...config }; const item = getFromStorage(key, storage, cfg); const storageState = item ? cfg.deserialize(item) : null; let hydrated = false; return (store) => { if (storageState != null && !hydrated) { const stateSignals = store['stateSignals']; const stateSignalKeys = Object.keys(stateSignals); const state = stateSignalKeys.reduce((state, key) => { const value = storageState[key]; return value ? { ...state, [key]: value } : state; }, getState(store)); patchState(store, state); hydrated = true; } effect(() => { const state = structuredClone(getState(store)); try { if (cfg.saveIf(state)) { cfg.excludeKeys.forEach((key) => { delete state[key]; }); storage.setItem(key, cfg.serialize(state)); } } catch (e) { cfg.error(e); } }); return store; }; } function getFromStorage(key, storage, cfg) { try { return storage.getItem(key); } catch (e) { cfg.error(e); return null; } } /** * Generated bundle index. Do not edit. */ export { getStorage, withStorage }; //# sourceMappingURL=larscom-ngrx-signals-storage.mjs.map