Hone logo
Hone
Problems

Persistent Cache Implementation in Vue with TypeScript

Building a persistent cache in a Vue application can significantly improve performance by reducing redundant API calls and providing a faster user experience. This challenge asks you to implement a simple persistent cache using Vue's reactivity system and localStorage to store cached data, ensuring data persists across browser sessions.

Problem Description

You need to create a usePersistentCache composable function in Vue.js (using TypeScript) that manages a cache for data. This cache should:

  1. Store data in localStorage: The cached data should be stored in the browser's localStorage under a specified key.
  2. Provide a reactive ref: The composable should return a reactive ref containing the cached data. Changes to this ref should trigger updates in the component using the composable.
  3. Handle initial loading: When the composable is first used, it should attempt to retrieve data from localStorage. If data is found, it should be loaded into the reactive ref. If not, the ref should be initialized as null or an appropriate default value.
  4. Allow data updates: The composable should provide a function to update the cached data. When this function is called, the new data should be stored in localStorage and the reactive ref should be updated.
  5. Handle errors gracefully: If there's an error accessing localStorage (e.g., localStorage is disabled), the composable should log the error and continue to function, initializing the ref with a default value (e.g., null).

Expected Behavior:

  • The first time the composable is used, it checks localStorage. If the key exists, the data is loaded into the reactive ref. Otherwise, the ref is initialized to null.
  • When data is updated using the provided function, the new data is stored in localStorage and the reactive ref is updated immediately.
  • Subsequent uses of the composable will load data from localStorage if the key exists.
  • If localStorage is unavailable, the composable should log an error and initialize the ref to null.

Examples

Example 1:

Input: usePersistentCache('user_data', () => fetch('/api/user').then(res => res.json()))
Output: { data: { name: 'John Doe', email: 'john.doe@example.com' }, updateData: (newData: any) => { ... } }
Explanation: The composable fetches user data from the API, stores it in localStorage under the key 'user_data', and provides a reactive ref for the data.  Subsequent calls will load from localStorage.

Example 2:

Input: usePersistentCache('settings', { theme: 'light' })
Output: { data: { theme: 'light' }, updateData: (newData: any) => { ... } }
Explanation: The composable initializes the cache with the provided settings object.  This object is stored in localStorage under the key 'settings'.

Example 3: (localStorage disabled)

Input: usePersistentCache('cart', () => []) // localStorage is disabled
Output: { data: null, updateData: (newData: any) => { ... } }
Explanation:  Because localStorage is disabled, the composable logs an error and initializes the data ref to null.

Constraints

  • Data Size: Assume the data stored in localStorage will be relatively small (less than 5MB). While localStorage can store more, excessively large data can impact performance.
  • Key Format: The cache key should be a string.
  • Error Handling: The composable must handle errors related to localStorage access gracefully.
  • Performance: The composable should be efficient and avoid unnecessary re-renders. Use Vue's reactivity system effectively.
  • TypeScript: The solution must be written in TypeScript.

Notes

  • Consider using JSON.stringify and JSON.parse to serialize and deserialize data when storing and retrieving from localStorage.
  • Think about how to handle asynchronous data fetching within the composable.
  • The updateData function should update both the localStorage and the reactive ref.
  • Focus on creating a reusable and well-structured composable function.
  • You don't need to implement the API fetching logic itself; the composable should accept a function that returns a promise resolving to the data. This allows for flexibility in how the data is obtained.
Loading editor...
typescript