DEV Community

Roberto Garella
Roberto Garella

Posted on

The Rolling Stores, an easy way to improve Web Storage API usage

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

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.

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

const session = true;
const mystore = trs(session);
Enter fullscreen mode Exit fullscreen mode

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

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";
Enter fullscreen mode Exit fullscreen mode

and reading them:

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
Enter fullscreen mode Exit fullscreen mode

trs guarantees the original type.

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

remove method delete a property:

mystore.remove("prop1");
console.log(mystore.prop1); // null
Enter fullscreen mode Exit fullscreen mode

subscribe gives the possibility to listen to value changes of a specific property: the callback will be executed every value changing.

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

// To unsubscribe
subscription.unsubscribe();
Enter fullscreen mode Exit fullscreen mode

clear method deletes all properties added into store.

mystore.clear();
Enter fullscreen mode Exit fullscreen mode

Here The Rolling Store source code:

// /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;
    }
  });
};
Enter fullscreen mode Exit fullscreen mode

Go to Codesandbox to play with The Rolling Store

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

Top comments (0)