useLocalStorage Deep Dive
The useLocalStorage hook provides a reactive interface for interacting with the browser's localStorage API, ensuring synchronization across different components and even different tabs.
Core Concepts
The hook follows a pattern similar to React's useState, but persists the value in the browser's storage.
import { useLocalStorage } from '@archibald/storefront';
function MyComponent() {
const [name, setName, clearName] = useLocalStorage('user-name');
return (
<div>
<p>Stored Name: {name}</p>
<input onChange={(e) => setName(e.target.value)} />
<button onClick={clearName}>Clear</button>
</div>
);
}
Reactivity & Synchronization
Unlike direct localStorage.getItem() calls, useLocalStorage is reactive. It uses useSyncExternalStore to subscribe to storage changes.
- Multi-Component Sync: If two components use the same key, updating the value in one will automatically trigger a re-render in the other.
- Cross-Tab Sync: The hook listens for the native browser
storageevent. If a user updates the value in a different tab, the hook will detect the change and update the UI in the current tab.
SSR Compatibility
useLocalStorage is designed to be safe for Server-Side Rendering.
- Server-Side: Since
localStorageis a browser-only API, the hook returns an empty string ('') on the server. - Hydration: The value is retrieved from storage only once the component hydrates in the browser, preventing hydration mismatches.
How it Works Step-by-Step
- Subscription: Upon mounting in the browser, the hook adds an event listener for the native
storageevent. - Initial Read: It calls
LocalStorageHelper.get(key)to retrieve the current value. - State Management: It uses
useSyncExternalStoreto manage the local state, ensuring it stays in sync with the external "source of truth" (localStorage). - Updating: When
set(value)is called:- It uses
LocalStorageHelper.set(key, value)to update the actual browser storage. - It dispatches a manual
StorageEvent. This is necessary because the nativestorageevent only fires for changes made in other windows/tabs. Dispatching it manually ensures components in the current window also update.
- It uses
- Clearing: When
clear()is called, it deletes the key and dispatches a storage event with an empty value. - Cleanup: When the component unmounts, the event listener is removed to prevent memory leaks.
Best Practice: Use useLocalStorage for non-sensitive user preferences (e.g., theme choice, dismissed banners) rather than sensitive data or large state objects.