Hone logo
Hone
Problems

Create a Custom useLocalStorageState Hook in React with TypeScript

This challenge asks you to build a custom React hook, useLocalStorageState, that mirrors the functionality of useState but persists its value across browser refreshes using the browser's localStorage. This is a common pattern for managing user preferences, application settings, or any data that should survive page reloads, and understanding how to build it is a valuable skill.

Problem Description

You need to create a reusable React hook called useLocalStorageState. This hook should accept a key (string) and an initial value as arguments. It should behave like the standard useState hook, providing a state variable and a function to update it. However, the hook must also persist the state value in localStorage using the provided key, and load the state from localStorage when the component initially mounts.

Key Requirements:

  • Persistence: The state value should be stored in localStorage under the provided key.
  • Loading from localStorage: On initial mount, the hook should attempt to retrieve the state value from localStorage using the provided key. If a value exists, it should be used as the initial state. If no value exists, the provided initial value should be used.
  • Update Behavior: When the update function is called, the state variable should be updated, and the new value should be immediately saved to localStorage.
  • TypeScript: The hook must be written in TypeScript, with appropriate type definitions for the key, initial value, state variable, and update function.
  • Error Handling: Gracefully handle potential errors when accessing localStorage (e.g., localStorage is disabled or full). If an error occurs during loading, use the provided initial value. If an error occurs during saving, log the error to the console (but do not throw an error, as this could disrupt the application).

Expected Behavior:

  1. On initial mount:
    • If a value exists in localStorage for the given key, the hook should initialize the state with that value.
    • If no value exists in localStorage for the given key, the hook should initialize the state with the provided initial value.
  2. When the update function is called:
    • The state variable should be updated with the new value.
    • The new value should be saved to localStorage under the given key.
  3. If localStorage is unavailable or an error occurs during loading or saving, the application should continue to function without crashing.

Examples

Example 1:

Input: useLocalStorageState("theme", "light")
Output: [state: "light", setState: (newValue: string) => void]
Explanation: If "theme" exists in localStorage, the state will be its value. Otherwise, the state will be initialized to "light".

Example 2:

Input: useLocalStorageState("count", 0)
Output: [state: 0, setState: (newValue: number) => void]
Explanation: If "count" exists in localStorage, the state will be its value (parsed as a number). Otherwise, the state will be initialized to 0.

Example 3: (Edge Case - localStorage unavailable)

Input: useLocalStorageState("settings", { showNotifications: true })
Output: [state: { showNotifications: true }, setState: (newValue: { showNotifications: boolean }) => void]
Explanation: If localStorage is unavailable, the state will be initialized to { showNotifications: true }.  Subsequent attempts to save will log an error to the console.

Constraints

  • The key must be a string.
  • The initial value can be of any type, but it must be serializable to a string (e.g., JSON.stringify-able).
  • The hook should be performant; avoid unnecessary re-renders.
  • Error handling should not crash the application. Errors should be logged to the console.
  • The hook should be compatible with functional components in React.

Notes

  • Consider using JSON.stringify and JSON.parse to store and retrieve complex data types from localStorage.
  • Think about how to handle potential type mismatches when retrieving data from localStorage.
  • The localStorage API is synchronous.
  • Remember to handle the case where localStorage might be disabled or unavailable.
  • Focus on creating a clean, reusable, and well-typed hook.
Loading editor...
typescript