josemi
projects notes
React Hooks and Local Storage
A guide for creating React Hooks to access to the Local and Session Storage of the browser.
September 24, 2023
What is Local and Session storage?
Local storage and Session storage are two web storage options available in JavaScript that allow you to store data on the client-side (in the user's browser) for a specific web domain. They are part of the Web Storage API and provide a way to store key-value pairs persistently (local storage) or for the duration of a page session (session storage).
On the one side, data stored using local storage persists even after the browser is closed, and is available across multiple browser sessions. It is suitable for storing user preferences, cached data, or application state.
On the other side, data stored with session storage is automatically cleared when the session ends (e.g., when the user closes the browser tab or window). It is suitable for temporary data that is needed only while the user is interacting with the current page, such as shopping cart items, form data, or temporary user authentication tokens.
API for local and session storage
The API for local ans session storage is exactly the same. Local storage API can be accessed from window.localStorage, and session storage API can be accessed from window.sessionStorage.
Both session storage and local storage offer similar methods for managing stored data. Here are the key methods available for both:
setItem(key, value)
Use this method to store a key-value pair in either session storage or local storage. The key argument is a string that acts as the identifier, and value can be any data type, but it will be automatically converted to a string.
getItem(key)
Use this method to retrieve the value associated with a specific key. It returns the stored value as a string.
removeItem(key)
Use this method to remove a specific key-value pair from either session storage or local storage.
// Storing data in local storage
localStorage.setItem("username", "John");

// Retrieving data from local storage
const username = localStorage.getItem("username"); // Returns "John"

// Removing data from local storage
localStorage.removeItem("username");
Creating the React Hook
Now, let's create a custom React hook to simplify working with local storage or session storage in React components, by encapsulating the logic for reading and writing values.
import {useState, useEffect} from "react";

export const useStorage = (store, key, defaultValue) => {
    // Initialize the state with the current value from storage or the default value
    const [currentValue, setCurrentValue] = useState(() => {
        // Attempt to retrieve the value from storage
        const storedValue = store.getItem(key);
        // If the value exists in storage, parse and return it
        if (storedValue !== null) {
            return JSON.parse(storedValue);
        }
        // Otherwise, return the default value
        return defaultValue;
    });
    // Update the storage value whenever currentValue changes
    useEffect(() => {
        // Serialize the new value and store it
        store.setItem(key, JSON.stringify(currentValue));
    }, [currentValue]);
    // Return the pair [currentValue, setCurrentValue]
    return [currentValue, setCurrentValue];
};
This hook follows these steps:
1. Initializes the state
It initializes the state (currentValue) with the value from storage if it exists, or uses the default value provided as the third argument of the hook.
2. Sets up an effect
It sets up an effect that runs whenever currentValue changes. This effect serializes the new value and stores it in the chosen storage (either local or session).
3. Returns the pair [currentValue, setCurrentValue]
It returns the pair [currentValue, setCurrentValue], allowing you to read and update the value in storage.
We can improve the previous code by creating two wrappers for using directly local or session storage, without having to provide this value as an argument to the hook:
export const useLocalStorage = (key, defaultValue) => {
    return useStorage(window.localStorage, key, defaultValue);
};

export const useSessionStorage = (key, defaultValue) => {
    return useStorage(window.sessionStorage, key, defaultValue);
};
This way, we can use the useLocalStorage and useSessionStorage hooks without having to pass the storage object as an argument.
import React from "react";
import {useLocalStorage} from "./useStorage.js";

export const MyCounter = () => {
    // Use the useLocalStorage hook to manage a value in local storage
    const [count, setCount] = useLocalStorage("count", 0);

    return (
        <div className="flex gap-2">
            <button onClick={() => setCount(prev => prev - 1)}>
                <span>-</span>
            </button>
            <div>{count}</div>
            <button onClick={() => setCount(prev => prev + 1)}>
                <span>+</span>
            </button>
        </div>
    );
};