Javascript how to improve reusable helper function for localstorage when data types differ?

Viewed 702

I have written a localstorage helper for my react application to make it reusable which doesnt feel very reusable. If I save or remove an item, it depends on the storage data type: If its array, I iterate through it, if its an Object or String, I cannot do the same. So Now I have separate functions (saveItemToArray, saveItemToObject etc). I tried also to write a generale function where I check the data type and based on that perform different actions but that also didnt seem practical. Is there a good solution? Thanks a lot!

export const localStorageHelper = {
    load: (key) => {
        return JSON.parse(localStorage.getItem(key))
    },
    saveToArr: (item, storageID) => {
        let storage = localStorageHelper.load(storageID);
        if (!storage) storage = [];
        localStorage.setItem(storageID, JSON.stringify([...storage, item]))
    },

    saveItemToObject: (item, storageID) => {
        let storage = localStorageHelper.load(storageID);
        if (!storage) storage = {};
        localStorage.setItem(storageID, {...storage, item});
    },
    removeItemFromArray: (removedItem, storageID) => {
        let storage = localStorageHelper.load(storageID);
        let updatedStorage = storage.filter((item)=> item !==removedItem);
        localStorage.setItem(storageID, JSON.stringify(updatedStorage))
    },
    //...
};
2 Answers

There are more shared parts that you factor out:

export const localStorageHelper = {
    load(key) {
        const stored = localStorage.getItem(key);
        return stored == null ? undefined : JSON.parse(stored);
    },
    store(key, value) {
        localStorage.setItem(key, JSON.stringify(value));
    },
    modify(key, fn) {
        this.store(key, fn(this.load(key)));
    },
    appendItemToArray: (item, storageID) => {
        this.modify(storageID, (storage = []) => [...storage, item]);
    },
    removeItemFromArray: (item, storageID) => {
        this.modify(storageID, (storage = []) => storage.filter(s => s !== item));
    },
    saveItemToObject: (item, storageID) => {
        this.modify(storageID, (storage = {}) => ({...storage, item}));
    },
    //...
};

But yes, having separate methods that do different things is the better approach. Instead of a general method that tries to be clever depending on the kind of data it sees, they do exactly what you want - and will fail if data types don't match instead of doing unpredictable things.

I've created a reusable storage service (pure and not a third party) for this and you can use it easily and save any data (that can be converted to JSON) in it.

Benefits:
1- Errors are handled and you can extend it
2- Save non-primitive data like objects
3- Set or remove multiple items

Usage:

import { ls } from "storage-service";

ls.set("user-data" , { name: "John Doe" })  // you don't need JSON.stringify
ls.get("user-data")  // you don't need JSON.parse

// Output:  { name: "John Doe" }

Related