The Rolling Stores, an easy way to improve Web Storage API usage
Tags: proxy, webstorage, localstorage, sessionstorage
Published: 2022-07-04 08:35:32

With their 5MB, localStorage and sessionStorage are great places to store data. You can add or remove item, and also decide to save data along the browser/tab session (sessionStorage) or persist them even browser will be closed (localStorage).

They help devs to improve Web Performance:

- Reduce network traffic
- Significantly speed up display times
- Cache data from RPC calls
- Load cached data on startup (faster startup)
- Save temporary state
- Restore state upon app reentry
- Prevent work loss from network disconnects

If you want to learn all about Storage Web API, please read: [MDN Storage Web API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API)

However both storages have a great limit: **all** items must be string. Numbers, Arrays, Objects... must be convert in string!
For this reason work with localStorage/sessionStorage can be frustrated: always need to remember to reconvert to the original item type.

_The Rolling Store_ (trs) gives the opportunity to work with localStorage/sessionStorage as you work with a simple object.

```js
import trs from "/path/to/the-rolling-store";

const session = true;
const mystore = trs(session);
```
`session` is a boolean and tells to The Rolling Store which Storage has to use: `true` for sessionStorage, `false` localStorage. Default: `true`. 

Now, you can add properties as you prefer

```js
mystore.prop1 = "prop1"; // string
mystore.prop2 = 12.99; // number
mystore.prop3 = true; // boolean
mystore.prop4 = {
  x: 1,
  y: "hello, world",
  z: false
}; // object
mystore["prop5"] = "prop5";
```
and reading them:
```js
console.log(mystore.prop1); // prop1
console.log(mystore.prop2); // 12.99
console.log(mystore.prop3); // true
console.log(mystore.prop4); 
/*
{
  x: 1,
  y: "hello, world",
  z: false
}
*/
console.log(mystore["prop5"]); // prop5
```
`trs` guarantees the original type.

`trs` exposes the following methods: `remove`, `subscribe` and `clear`.

`remove` method delete a property:

```js
mystore.remove("prop1");
console.log(mystore.prop1); // null
```
`subscribe` gives the possibility to listen to value changes of a specific property: the callback will be executed every value changing.

```js
const subscription = mystore.subscribe("prop2", (val) => {
  console.log("prop2", val);
});

// To unsubscribe
subscription.unsubscribe();
```

`clear` method deletes **all properties** added into store.

```js
mystore.clear();
```
Here _The Rolling Store_ source code:

```js
// /path/to/the-rolling-store.js

import { BehaviorSubject } from "rxjs";

export default (session = true) => {
  const storage = session ? sessionStorage : localStorage;
  const subjects = {};

  const getSubject = (prop, value) => {
    if (!(prop in subjects)) {
      subjects[prop] = new BehaviorSubject(value);
    }
    return subjects[prop];
  };

  const pubUpdate = (prop, value) => {
    getSubject(prop).next(value);
  };

  const getSubscription = (prop, callback) => {
    return getSubject(prop).subscribe((v) => callback(v));
  };

  const subscribe = new Proxy(getSubscription, {
    apply(target, thisArg, args) {
      return target(...args);
    }
  });

  const remove = new Proxy((prop) => storage.removeItem(prop), {
    apply(target, thisArg, args) {
      return target(...args);
    }
  });

  const clear = new Proxy(storage.clear, {
    apply(target, thisArg, args) {
      return target(...args);
    }
  });

  const key = new Proxy((index) => storage.key(index), {
    apply: (target, thisArg, args) => {
      return target(...args);
    }
  });

  const methodsMap = {
    subscribe,
    remove,
    clear,
    key
  };

  return new Proxy(storage, {
    get(store, prop) {
      if (prop in methodsMap) {
        return methodsMap[prop];
      }
      return JSON.parse(store.getItem(prop));
    },
    set(store, prop, value) {
      store.setItem(prop, JSON.stringify(value));
      pubUpdate(prop, value);
      return true;
    }
  });
};
```
Go to _Codesandbox_ to play with [The Rolling Store](https://codesandbox.io/s/the-rolling-store-1f2nr?file=/index.html)

Feel free to correct or suggest improvements. It's a starting point... and this is my first tech article in english! 😱