use-local-storage-state

repository·main·Indexed 22 days ago

https://github.com/astoilkov/use-local-storage-state

A React hook for persisting state in localStorage with support for SSR, cross-tab synchronization, and in-memory fallbacks. It provides a useState-like API that returns the current value, a setter function, and utility properties including isPersistent and removeItem. Version 20.0.0 supports React 18+, with a separate version for React 17 and below.

Tokens
1.7K
Snippets
5
Records
11
Agent score
79%

What's inside use-local-storage-state

  1. Handle hydration re-renders in SSR

    main

    When using SSR (e.g., Next.js), components using useSyncExternalStore (which this library uses) may render twice during hydration. This is standard React behavior. To detect if you are currently rendering the server value, you can use this helper:

    function useIsServerRender() {
      return useSyncExternalStore(() => {
        return () => {}
      }, () => false, () => true)
    }
  2. Configure useLocalStorageState options

    main

    The useLocalStorageState hook accepts an optional options object of type LocalStorageOptions:

    • defaultValue: The initial value if no value exists in localStorage. Similar to useState default value.
    • defaultServerValue: The value used during SSR and hydration. If not set, it defaults to defaultValue.
    • storageSync: A boolean (default true). If set to false, the hook will not subscribe to the Window storage event, meaning updates will not sync across different tabs, windows, or iframes.
    • serializer: An object { stringify, parse } (default JSON). Use this to support complex types like Date, RegExp, or BigInt by providing a library like superjson.
  3. Use the inMemoryData fallback

    main

    When localStorage is inaccessible (due to browser security settings or quota limits), the library uses an internal inMemoryData Map to store state. This ensures the application remains functional even if persistence is unavailable.

    If you need to check if the current state is actually being persisted to disk or just living in memory, check the isPersistent property returned by the hook.

    • isPersistent: true -> Data is in localStorage.
    • isPersistent: false -> Data is in inMemoryData (fallback).
  4. Use removeItem() to reset state

    main

    The removeItem() method, returned in the third element of the hook's return array, removes the key from localStorage and resets the hook's state to its defaultValue.

    import useLocalStorageState from 'use-local-storage-state'
    
    export default function Todos() {
        const [todos, setTodos, { removeItem }] = useLocalStorageState('todos', {
            defaultValue: ['buy avocado']
        })
    
        function onClick() {
            removeItem()
        }
    }
  5. Use the useLocalStorageState hook

    main

    The useLocalStorageState hook provides a way to persist state in localStorage. It returns an array containing the current value, a setter function, and an object with utility properties.

    Basic usage:

    import useLocalStorageState from 'use-local-storage-state'
    
    const [todos, setTodos] = useLocalStorageState('todos', {
        defaultValue: ['buy avocado', 'do 50 push-ups']
    })
    import useLocalStorageState from 'use-local-storage-state'
    
    export default function Todos() {
        const [todos, setTodos] = useLocalStorageState('todos', {
            defaultValue: ['buy avocado', 'do 50 push-ups']
        })
    }
  6. Check if data is persistent with isPersistent

    main

    If localStorage is unavailable (e.g., due to browser settings or errors), the hook falls back to in-memory storage. You can use the isPersistent boolean property to detect this state and notify the user that their changes will not be saved across sessions.

    import React, { useState } from 'react'
    import useLocalStorageState from 'use-local-storage-state'
    
    export default function Todos() {
        const [todos, setTodos, { isPersistent }] = useLocalStorageState('todos', {
            defaultValue: ['buy avocado']
        })
    
        return (
            <>
                {todos.map(todo => (<div key={todo}>{todo}</div>))}
                {!isPersistent && <span>Changes aren't currently persisted.</span>}
            </>
        )
    }
  7. Use the useLocalStorageState hook

    main

    The useLocalStorageState hook provides a way to manage state that is synchronized with localStorage. It returns a tuple similar to React's useState, but with additional metadata for managing persistence.

    Return Value Structure: [value, setState, { isPersistent, removeItem }]

    • value: The current state value.
    • setState: A function to update the state (supports functional updates like useState).
    • isPersistent: A boolean indicating if the data is currently stored in localStorage. If localStorage is unavailable (e.g., due to security settings or quota limits), the hook falls back to an in-memory store, and isPersistent will be false.
    • removeItem: A function to remove the item from storage.

    Key Features:

    • Automatic Synchronization: Syncs state across different tabs, windows, and iframes via the storage event.
    • Resilient Fallback: If localStorage throws an error (e.g., Safari private mode or disabled cookies), it automatically falls back to an in-memory Map to prevent application crashes.
  8. Define LocalStorageState type

    main

    The LocalStorageState<T> type defines the shape of the array returned by the hook:

    export type LocalStorageState<T> = [
        T,                               // The current state value
        Dispatch<SetStateAction<T>>,     // The setter function
        {
            isPersistent: boolean,      // Whether data is in localStorage or in-memory fallback
            removeItem: () => void      // Function to clear the key from storage
        },
    ]