Store Features
Sync the state of the store to the web storage
Kind: global function
Import the withCalls trait from @ngrx-traits/signals.
import { withSyncToWebStorage } from '@ngrx-traits/signals';const store = signalStore(
// following are not required, just an example it can have anything
withEntities({ entity, collection }),
withCallStatus({ prop: collection, initialValue: 'loading' }),
// Sync the store to session storage
withSyncToWebStorage({
key: 'my-key',
type: 'session',
// type: 'local', // to use local storage
}),
);const store = signalStore(
// following are not required, just an example it can have anything
withEntities({ entity, collection }),
withCallStatus({ prop: collection, initialValue: 'loading' }),
withSyncToWebStorage({
key: 'my-key',
type: 'session',
expires: 5000,
// filter the state before saving to the storage
filterState: ({ orderItemsEntityMap, orderItemsIds }) => ({
orderItemsEntityMap,
orderItemsIds,
}),
}),
);const store = signalStore(
// following are not required, just an example it can have anything
withEntities({ entity, collection }),
withCallStatus({ prop: collection, initialValue: 'loading' }),
withSyncToWebStorage({
key: 'my-key',
type: 'session',
restoreOnInit: true,
saveStateChangesAfterMs: 300,
expires: 1000 * 60 * 60 * 12, // 12 hours
}),
);You can use valueMapper to provide custom bidirectional transformation between the store state and the storage value. This is useful when you want to store only specific properties or transform the data before saving.
For example, in a form where you only want to persist certain nested fields:
const store = signalStore(
withState({
userProfile: {
userName: '',
email: '',
preferences: { theme: 'light', notifications: true },
tempData: null, // This won't be saved
}
}),
withSyncToWebStorage({
key: 'user-form',
type: 'local',
restoreOnInit: true,
saveStateChangesAfterMs: 500,
// Only save and restore userName and email from userProfile, not preferences or tempData
valueMapper: (store) => ({
stateToStorageValue: () => ({
userName: store.userProfile().userName,
email: store.userProfile().email,
}),
storageValueToState: (savedData) => {
patchState(store, {
userProfile: {
...store.userProfile(),
userName: savedData.userName,
email: savedData.email,
}
});
},
}),
}),
);Note: valueMapper and filterState are mutually exclusive - you can only use one or the other, not both.
You can add withSyncToWebStorage multiple times with different keys, this can be useful if you are creating your own store features and each has a withSyncToWebStorage, or you need to split the state in two keys.
import { signalStoreFeature } from '@ngrx/signals';
// two custom store features, each with its own withSyncToWebStorage and key
function withProductsList() {
const productsEntityConfig = entityConfig({
entity: type<Product>(),
collection: 'products',
});
return signalStoreFeature(
// following are not required, just an example it can have anything
withEntities(productsEntityConfig),
withCallStatus(productsEntityConfig),
withSyncToWebStorage({
key: 'my-products',
type: 'session',
restoreOnInit: true,
saveStateChangesAfterMs: 300,
expires: 1000 * 60 * 60 * 12, // 12 hours
}),
);
}
function withOrderList() {
const ordersEntityConfig = entityConfig({
entity: type<Order>(),
collection: 'orders',
});
return signalStoreFeature(
// following are not required, just an example it can have anything
withEntities(ordersEntityConfig),
withCallStatus(ordersEntityConfig),
withSyncToWebStorage({
key: 'my-orders',
type: 'session',
restoreOnInit: true,
saveStateChangesAfterMs: 300,
expires: 1000 * 60 * 60 * 12, // 12 hours
}),
);
}
const store = signalStore(
// using then will store each slice of state in its
// own store key
withProductsList(),
withOrderList()
);This trait receives an object to allow specific configurations:
| Property | Description | Value |
|---|---|---|
| key | Key to use when storing in session or local storage | string |
| type | Type of storage to use | 'session' | 'local' |
| restoreOnInit | Auto restore the state from the storage when the store is initialized | boolean. Default: true |
| saveStateChangesAfterMs | Milliseconds after which the state is saved to storage when it changes | number. Default: 500 |
| filterState | Optional function to filter the state signals that will be synced to storage (mutually exclusive with valueMapper) | ({state1}) => ({state1}) |
| valueMapper | Optional custom transformation between store state and storage value (mutually exclusive with filterState) | StorageValueMapper<T, Store> |
| expires | If the data is older than the time in milliseconds it won't be restored | number |
| onRestore | Optional callback after the state is restored from storage | (store) => void |
The valueMapper is a factory function that receives the store and returns an object with two methods:
valueMapper: (store: Store) => {
stateToStorageValue: () => T | undefined | null;
storageValueToState: (value: T) => void;
}| Property | Description | Type |
|---|---|---|
| stateToStorageValue | Function to transform store state to storage value | () => T | undefined | null |
| storageValueToState | Function to transform storage value back to state | (value: T) => void |
No extra state generated
No extra computed generated
saveToStorage: () => void;
loadFromStorage: () => void;
clearFromStore: () => void;