The right way to use LocalStorage in JavaScript
Being a web developer we all have been into a situation, where we want to persist a piece of information like user data, theme preference, or the selected filters, to give our users a consistent experience across browser sessions.
Well, that's exactly where the LocalStorage API comes into the picture.
Hold on, Hold on! Let's start from the basics
What is LocalStorage?
The LocalStorage API of web browsers allows to store and then read the stored data across browser sessions.
Let's break it down:
- It allows us to store data into persistent memory so that the data is still available when we restart the browser or even the computer.
- It stores the data locally to that origin, meaning you can only read/write the data to LocalStorage for the current origin i.e the following combination (protocol+domain+port)
It is to be noted that the LocalStorage starts empty and the items added during a private session get cleared as soon as the last private tab is closed.
Internals of LocalStorage
The LocalStorage is a key-value store, meaning it stores the given value against the provided key, just like a JavaScript object but persistent.
Key-Value store provides fast lookup and writes because of its structure finding the right element will always take constant time (apart from the time to do I/O). This means having hundreds of keys in your LocalStorage wouldn't slow down the lookup. (Not sure why you would be doing that.)
With its speed comes one limitation, the key
and value
both have to be a string to be stored in the LocalStorage.
Well, this isn't so hard to get around.
Currently the Web Storage specification allows you to store up to 5MB per app per browser.
How to use LocalStorage?
Thankfully the LocalStorage API is fairly simple to interface with.
Let's go ahead and see how we can do the basic operations like Create/Read/Update/Delete on LocalStorage,
Writing data
The localStorage.setItem()
accepts a string as key
and the value
is also accepted as string.
localStorage.setItem("<key>", "<value>");
The above line of code will write the value against the given key, if the already exists, the existing value will be overwritten.
Reading data
To read the stored information, we need to provide the key
const value = localStorage.getItem("<key>");
// value will be null or string
null
is returned if no data is found with the given key
.
Storing Objects in LocalStorage
You might be wondering, "Strings! Jeez, what am I going to do about an object?". Don't worry.
We are still allowed to store a serialized version of the object,
// storing an object in LocalStorage
const user = { name: "anshuman_bhardwaj" };
localStorage.setItem("user", JSON.stringify(user));
// reading the object from LocalStorage
const strinifiedUser = localStorage.getItem("user");
if (strinifiedUser) {
const retrivedUser = JSON.parse(strinifiedUser);
}
Deleting data
There are two methods for removing stored data from LocalStorage programmatically
removeItem
If you already know which item to delete, removeItem
is the way to go.
localStorage.removeItem("<key>");
clear
If you want to remove all keys from the storage, then clear
is a clear choice. (you see what I did there?)
localStorage.clear();
As exciting as it may sound, the clear
method shouldn't be used all that much because it clears everything and not just the items that you added.
This means if you are interacting with services that use LocalStorage e.g. authentication modules like Firebase Auth or Okta, clearing the LocalStorage will also delete the data those services had put in and it'll break their behavior.
Yeah, don't worry, I got you.
In computer science we should always focus on encapsulation, meaning we should hide the information or encapsulate it, so to say. Well, that's exactly how we are going to solve our little problem here.
The following pattern was suggested to me by my friend & colleague Ryan.
Creating and using Namespace in LocalStorage?
We can apply the principle of encapsulation here by putting all of our data under a predefined & unique key or namespace. This will allow us to hide our from other parts of the application (which we don't control) and also save us from mistakenly updating the data which we shouldn't.
"Sounds good, but how do we do that?", you might be wondering.
Well, it's simpler than it sounds and works by enclosing the whole application state under one single key rather than using a new key for each information.
Step 1
Create a key, predictable yet unique. A good example would be [your-app-name]+[some-unique-token] i.e. DEV-007
Step 2
While storing information, we read the namespace value from the LocalStorage, deserialize it, update the value against the key inside the object and then serialize it again before writing to LocalStorage.
Step 3
While reading the information, we read the namespace value from the LocalStorage, deserialize it and return the value of the key from the object.
This way we treat the namespace like it's own little LocalStorage.
Below is a code implementation of the above
const NAMESPACE = "DEV-007";
function writeToStorage(key, value) {
const serializedData = localStorage.getItem(NAMESPACE);
const data = serializedData ? JSON.parse(serializedData) : {};
data[key] = value;
localStorage.setItem(NAMESPACE, JSON.stringify(data));
}
function readFromStorage(key) {
const serializedData = localStorage.getItem(NAMESPACE);
const data = JSON.parse(serializedData);
return data ? data[key] : undefined;
}
function clear() {
localStorage.setItem(NAMESPACE, JSON.stringify({}));
}
function removeItem(key) {
const serializedData = localStorage.getItem(NAMESPACE);
const data = serializedData ? JSON.parse(serializedData) : {};
delete data[key];
localStorage.setItem(NAMESPACE, JSON.stringify(data));
}
The above implementation of clear
and removeItem
is safe to use and solves our issue.
Don't worry you will not have to write your own implementation because there exists an npm package store2 that solves the above problem and provides smarter localstorage.
That's all for today. Should you have any questions or suggestions please feel free to drop them in the comments below. For more such content, please follow me on Twitter
Until next time