Implementing a useLocalStorage Hook in Vue with TypeScript
This challenge asks you to create a custom Vue Composition API hook, useLocalStorage, that provides a simple and type-safe way to interact with the browser's localStorage. This hook will handle reading, writing, and deleting data from localStorage while ensuring type safety and providing reactivity within your Vue components. It's a common pattern for persisting component state across page reloads.
Problem Description
You need to implement a useLocalStorage hook in Vue 3 using TypeScript. This hook should accept a key (string) and an initial value (of any type) as arguments. It should then:
- Read from
localStorage: Attempt to read a value associated with the provided key fromlocalStorage. If a value exists, parse it based on its type (string, number, boolean, or object/array - stringified). If no value exists, use the provided initial value. - Provide a Reactive Reference: Return a
refcontaining the current value. Thisrefshould be reactive, meaning changes to the value should trigger updates in any components using the hook. - Provide Setter and Deleter Functions: Return an object containing two functions:
set(newValue: T): A function that accepts a new value of the same type as the initial value (T). This function should serialize the value to a string (if it's an object or array) and store it inlocalStorageassociated with the provided key. It should also update the reactiverefwith the new value.delete(): A function that removes the key-value pair fromlocalStorageand resets the reactiverefto its initial value.
Key Requirements:
- Type Safety: The hook should be type-safe, ensuring that the
setfunction only accepts values of the correct type. - Reactivity: Changes to the value should be reactive within Vue components.
- Error Handling: Handle potential errors during
localStorageaccess (e.g.,localStoragebeing unavailable). In such cases, use the initial value. - Serialization/Deserialization: Correctly serialize objects and arrays to strings before storing them in
localStorageand deserialize them when reading.
Expected Behavior:
- The hook should correctly read and write values to
localStorage. - Changes to the value in
localStorageshould be reflected in the reactiveref. - The
deletefunction should remove the key-value pair fromlocalStorageand reset theref. - The hook should handle edge cases gracefully, such as
localStoragebeing unavailable or invalid data inlocalStorage.
Examples
Example 1:
Input: useLocalStorage('myNumber', 0)
Output: { value: Ref<number>, set: (newValue: number) => void, delete: () => void }
Explanation: Initializes a reactive ref with the value 0, stored under the key 'myNumber'. If 'myNumber' exists in localStorage, it will be read and parsed as a number.
Example 2:
Input: useLocalStorage('myObject', { name: 'John', age: 30 })
Output: { value: Ref<{ name: string; age: number; }>, set: (newValue: { name: string; age: number; }) => void, delete: () => void }
Explanation: Initializes a reactive ref with the object { name: 'John', age: 30 }, stored under the key 'myObject'. If 'myObject' exists in localStorage, it will be read, parsed as JSON, and assigned to the ref.
Example 3: (Edge Case - localStorage unavailable)
Input: useLocalStorage('myString', 'Hello') when localStorage is disabled in the browser.
Output: { value: Ref<string>, set: (newValue: string) => void, delete: () => void }
Explanation: The hook should initialize the ref with the initial value 'Hello' and not attempt to access localStorage.
Constraints
- The hook must be implemented using Vue 3's Composition API and TypeScript.
- The initial value can be of any valid JavaScript type (string, number, boolean, object, array, etc.).
- The hook should not introduce any external dependencies.
- The serialization/deserialization process should be efficient enough for common use cases. Avoid complex or computationally expensive serialization methods.
- The hook should be compatible with all modern browsers that support
localStorage.
Notes
- Consider using
JSON.stringifyandJSON.parsefor serializing and deserializing objects and arrays. - Remember to handle potential errors when accessing
localStorage. - Think about how to ensure that the reactive
refis updated correctly when the value inlocalStoragechanges. - The
deletefunction should reset therefto the initial value, notnullorundefined. - Type safety is crucial. Use generics to ensure that the
setfunction only accepts values of the correct type.