JAVASCRIPT
React useLocalStorage Hook for Persisting State Across Sessions
Learn to build a `useLocalStorage` React hook to easily store and retrieve component state from the browser's local storage, perfect for user preferences or persistent data.
import { useState, useEffect, useCallback } from 'react';
function useLocalStorage(key, initialValue) {
// State to store our value
// Pass initial state function to useState so logic is only executed once
const [storedValue, setStoredValue] = useState(() => {
try {
if (typeof window === 'undefined') {
return initialValue;
}
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch (error) {
console.error(error);
return initialValue;
}
});
// Return a wrapped version of useState's setter function that ...
// ... persists the new value to localStorage.
const setValue = useCallback((value) => {
try {
if (typeof window === 'undefined') {
console.warn("localStorage is not available.");
return;
}
// Allow value to be a function so we have same API as useState
const valueToStore =
value instanceof Function ? value(storedValue) : value;
// Save state
setStoredValue(valueToStore);
// Save to local storage
window.localStorage.setItem(key, JSON.stringify(valueToStore));
} catch (error) {
console.error(error);
}
}, [key, storedValue]); // Dependency `storedValue` ensures latest state for functional updates
// Optional: Add an effect to listen for changes from other tabs/windows
useEffect(() => {
const handleStorageChange = (event) => {
if (event.key === key) {
try {
setStoredValue(event.newValue ? JSON.parse(event.newValue) : initialValue);
} catch (error) {
console.error("Error parsing storage change:", error);
}
}
};
if (typeof window !== 'undefined') {
window.addEventListener('storage', handleStorageChange);
return () => {
window.removeEventListener('storage', handleStorageChange);
};
}
}, [key, initialValue]);
return [storedValue, setValue];
}
// Example Usage:
/*
import React from 'react';
import useLocalStorage from './useLocalStorage'; // Assuming useLocalStorage is in its own file
function ThemeSwitcher() {
const [theme, setTheme] = useLocalStorage('app-theme', 'light');
const toggleTheme = () => {
setTheme(theme === 'light' ? 'dark' : 'light');
};
return (
<div style={{ background: theme === 'dark' ? '#333' : '#fff', color: theme === 'dark' ? '#fff' : '#333', padding: '20px' }}>
<h1>Current theme: {theme}</h1>
<button onClick={toggleTheme}>Toggle Theme</button>
</div>
);
}
*/
How it works: The `useLocalStorage` hook initializes state by attempting to retrieve a value from `localStorage` based on a given `key`. If found, it parses and uses that value; otherwise, it uses the `initialValue`. The `setValue` function is a wrapped `setStoredValue` that also serializes and saves the new value to `localStorage`. A `useEffect` hook also listens for `storage` events, allowing changes made in other tabs or windows to synchronize the state in the current component, ensuring consistency across the application.