@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
JavaScript
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