Persist State

The persistState() function gives you the ability to persist some of the app’s state, by saving it to localStorage/sessionStorage or anything that implements the StorageEngine API, and restore it after a refresh.

To use it you should call the persistState() function in the main.ts file:

main.ts
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { enableAkitaProdMode, persistState } from '@datorama/akita';
if (environment.production) {
enableProdMode();
enableAkitaProdMode();
}
persistState();
platformBrowserDynamic()
.bootstrapModule(AppModule)
.catch(err => console.log(err));

Options

  • storage - storage strategy to use. This defaults to localStorage but you can pass sessionStorage or anything that implements the StorageEngine API.

  • key - The key by which the state is saved.

  • include - by default the whole state is saved to storage, use this param to include only the stores you need. It can be a store name or a predicate callback.

  • deserialize: Custom deserializer. Defaults to JSON.parse

  • serialize: Custom serializer, defaults to JSON.stringify

  • preStorageUpdateOperator: Custom operators that will run before storage update

  • enableInNonBrowser:Whether to enable persistState in a non-browser environment

The whole state is defined by the state of every Akita store in your application

Sometimes you may want to clear one of the stores from the storage. persistState() returns an object with the following methods:

export const storage = persistState();
storage.destroy(); // Stop sync the state
storage.clearStore(); // Clear the storage
storage.clearStore('todos'); // Clear the todos store from storage

You can also track a specific store's key:

persistState({
include: ['auth.token']
});

Custom Hooks

You can use custom hooks to save specific keys, for example:

persistState({
include: ['auth', 'todos'],
preStorageUpdate: function(storeName, state) {
if(storeName === 'auth') {
return {
token: state.token,
expired: state.expired
}
}
return state;
},
preStoreUpdate(storeName: string, state: any) {
return state;
}
});

Async Support

We’ve add asynchronous support to the persistState plugin. This gives you the option to save a store’s value to a persistent storage, such as indexDB, websql, or any other asynchronous API. Here’s a live demo that leverages localForage:

import * as localForage from 'localforage';
localForage.config({
driver: localForage.INDEXEDDB,
name: 'Akita',
version: 1.0,
storeName: 'akita-storage'
});
persistState({ include: ['auth.token', 'todos'], storage: localForage });

Akita also exposes the selectPersistStateInit observable. This observable emits after Akita initialized the stores based on the storage's value. For example:

import { selectPersistStateInit } from '@datorama/akita';
export class AuthGuard {
constructor(private router: Router, private authQuery: AuthQuery) {
}
canActivate() {
return combineLatest([
this.authQuery.isLoggedIn$,
selectPersistStateInit(),
]).pipe(
map(([isAuth]) => {
if(isAuth) {
return true;
}
this.router.navigateByUrl('login');
return false;
}),
take(1)
);
}
}

Performance Optimization

By default, the plugin will update the storage upon each store's change. Some applications perform multiple updates in a second, and update the storage on each change can be costly. For such cases, it's recommended to use the preStorageUpdateOperator option and add a debounce. For example:

import { debounceTime } from 'rxjs/operators';
persistState({
include: ['auth.token', 'todos'],
preStorageUpdateOperator: () => debounceTime(2000)
});