JAVASCRIPT
Persisting State with useLocalStorage Custom Hook
Build a `useLocalStorage` custom hook in React to automatically synchronize and persist component state with the browser's local storage.
import React, { useState, useEffect } from 'react';
// Custom hook for persisting state to local storage
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;
}
// Get from local storage by key
const item = window.localStorage.getItem(key);
// Parse stored json or if none return initialValue
return item ? JSON.parse(item) : initialValue;
} catch (error) {
// If error also return initialValue
console.warn(`Error reading localStorage key “${key}”:`, error);
return initialValue;
}
});
// useEffect to update local storage when the state changes
useEffect(() => {
try {
if (typeof window !== 'undefined') {
window.localStorage.setItem(key, JSON.stringify(storedValue));
}
} catch (error) {
console.warn(`Error setting localStorage key “${key}”:`, error);
}
}, [key, storedValue]); // Only re-run if key or storedValue changes
return [storedValue, setStoredValue];
}
function ThemeToggler() {
const [theme, setTheme] = useLocalStorage('app-theme', 'light');
const toggleTheme = () => {
setTheme(prevTheme => (prevTheme === 'light' ? 'dark' : 'light'));
};
return (
<div style={{ backgroundColor: theme === 'dark' ? '#333' : '#f0f0f0', color: theme === 'dark' ? '#f0f0f0' : '#333', padding: '20px', minHeight: '100vh' }}>
<h1>Current Theme: {theme.toUpperCase()}</h1>
<button onClick={toggleTheme}>Toggle Theme</button>
<p>This theme preference is saved in local storage and persists across refreshes.</p>
</div>
);
}
export default ThemeToggler;
How it works: The `useLocalStorage` custom hook acts like `useState` but also synchronizes the state with the browser's `localStorage`. It attempts to retrieve the value from `localStorage` on initial render; if not found, it uses a provided initial value. Any subsequent updates to the state through the returned `setStoredValue` function will automatically update `localStorage`, ensuring data persistence across page refreshes.