State persistence in JavaScript — wora/cache-persist — Getting Started

lorenzo di giacomo
5 min readNov 16, 2020

--

Photo by Tomasz Frankowski on Unsplash

In this post I want to talk about my experience in managing persistence of the state in JavaScript.

As you all know, in the world of javascript there are many libraries / frameworks (and every day a new one is born, eg recoil) that allow in a more or less easy way to manage the state within your applications.

The ones I came across are redux, mobx, apollo & relay. What did all these libraries have in common?
None of them natively managed state persistence, and third-party libraries were created to handle it:

This has always bothered me a lot because every time besides having to learn / study a new state management library you have to hope that a persistence library has been created and then study this too.

It was thanks to the absence of a relay persistence library that I decided to create my wora/relay-store library by setting myself an ambitious goal of being able to standardize once and for all the persistence management of a state in javascript.
To achieve this, you need a library that:

  • abstracts the concept of state in order to natively integrate communication with any storage (web / react-native / custom, Sync & Async) without any effort for users
  • it is highly configurable
  • it is efficient
  • it is easily integrated into any other state management library

And that’s why today I’m introducing wora/cache-persist

wora/cache-persist

As already mentioned, this library allows you, with very little effort of work, to manage the state within your application and its persistence in storage.

All you need to know to use it is the Cache class and the configurations it makes available to you.

So why wait, let’s start right now by viewing the wora/cache-persist Cache:

As you can see from the interface above, there are 3 categories of functions:

  • to read: these functions, synchronous, allow you to read state data
  • to write: these functions, synchronous, allow you to write data in the state
  • to manage persistence: these functions allow you to manage the persistence of state within the storage

The read and write functions are very similar to those of the Map object, so I do not need to add anything else while the restore, isRehydrated and flush functions are described below so that you better understand when and how to use them.

  • restore: it is the most important function of all, in fact it must necessarily be used in order to connect the store to its reference storage and initialize the cache state. Apart from special situations, e.g. Server Side Rendering, this function is recommended to be called when starting the application.
  • flush: this function allows you to make sure that the storage is synchronized with state data.
  • isRehydrated: this function lets you know if the cache has been connected with the storage via the restore function

Well, now we can see how to use the cache within our application.

First, let’s install the packages we need:

  • wora/cache-persist using yarn or npm:
yarn add @wora/cache-persist

Now, I can show you the simplest use case:

simplest use case

That’s all :)

Obviously only in very rare cases are we lucky enough to have to manage such a simple case where we use the localStorage (or for ReactNative the AsyncStorage) without one of these problems:

  • How can I initialize the cache with data?
  • There is data in the storage that is not compatible with the new version of the application, how can I migrate it?
  • How can I use the same storage to manage different states? (e.g. different state based on the logged in user)
  • How can I use indexedDB?
  • How can I use session storage?
  • How can I prevent some state data from being saved within the storage?
  • How can I encrypt the data before it is saved in the storage?
  • How do I handle storage errors when saving data?

The library gives an answer to these problems (and not only these) through its configurations.

For this reason I will start by describing all its configurations and then I will show you how to use them to answer the problems indicated above

Cache Options:

prefix: [Optional][Default: cache] when this option is defined, the prefixLayer mutation included in the library is used in order to add / remove the prefix to persisted keys.

serialize: [Optional][Default: true] when this option is true, the jsonSerialize mutation included in the library is used in order to handles JSON serialization and deserialization of values

mutateKeys: [Optional] with this option you can configure an array of mutation functions that will be executed in order to change the keys while writing / reading in the storage. The prefix option will insert the prefixLayer function as the first in the list.

mutateValues: [Optional] with this option you can configure an array of mutation functions that will be executed in order to change the values while writing / reading in the storage. The serialize option will insert the jsonSerialize function as the last in the list.

webStorage: [Optional][Default: local] with this option it is possible to decide to use the sessionStorage by enhancing the property with session

storage: [Optional][Default: localStorage/AsyncStorage] when you need to use custom storage or the indexedb, this property must be set.

throttle: [Optional][Default: 500] Defines the expiration interval of the debounce

errorHandling: [Optional] Allows you to define the callback function that is executed in case errors are raised during the write process to the storage.

disablePersist: [Optional][Default: false] Allows you to use the cache as an inmemory cache.

cache options

How can I initialize the cache with data?

There is data in the storage that is not compatible with the new version of the application, how can I migrate it?

How can I use the same storage to manage different states? (e.g. different state based on the logged in user)

How can I use indexedDB?

How can I use session storage?

How can I prevent some state data from being saved within the storage?

How can I encrypt the data before it is saved in the storage?

How do I handle storage errors when saving data?

What’s next

For those interested in knowing how it was implemented, I recommend reading: State persistence in JavaScript — wora/cache-persist — Deep into its Source Code

How you can contribute

  • If you are a creator of a state management library, consider integrating this library
  • Give a star to the repository and share it, you will help the project and the people who will find it useful
  • Create issues, your questions are a valuable help
  • PRs are welcome, but it is always better to open the issue first so as to help me and other people evaluating it

--

--

lorenzo di giacomo
lorenzo di giacomo

Written by lorenzo di giacomo

Creator of Relay Hooks, React Relay Offline, Relay Angular & Wora. Solution Architect of enterprise applications in cloud native and j2ee environments