Hone logo
Hone
Problems

Implementing a Keep-Alive Cache in Vue with TypeScript

Many applications benefit from caching frequently accessed data to improve performance and reduce server load. This challenge asks you to implement a keep-alive cache within a Vue component, ensuring that data remains available for a specified duration, minimizing redundant API calls. This is particularly useful for data that doesn't change frequently and is accessed repeatedly.

Problem Description

You need to create a Vue component that utilizes a keep-alive cache to store and retrieve data. The component should accept a fetchFunction as a prop, which is an asynchronous function responsible for fetching the data from an external source (e.g., an API). The component should automatically cache the fetched data and return it, only re-fetching when the cache has expired or the component is re-rendered with a different fetchFunction.

Key Requirements:

  • Caching: Store the fetched data in a cache.
  • Expiration: Implement a time-to-live (TTL) for the cache. After the TTL expires, the cache should be considered invalid.
  • Automatic Re-fetching: Automatically re-fetch the data when the cache expires or the fetchFunction prop changes.
  • TypeScript: The solution must be written in TypeScript.
  • Reactive Updates: The component should reactively update its output when the cached data changes.
  • Error Handling: The fetchFunction might fail. The component should handle potential errors gracefully and provide a fallback mechanism (e.g., returning a default value or displaying an error message).

Expected Behavior:

  1. When the component is initially mounted, it should call the fetchFunction to retrieve the data.
  2. The fetched data should be stored in the cache with the specified TTL.
  3. Subsequent requests for the data within the TTL should return the cached data without calling the fetchFunction again.
  4. When the TTL expires, the next request should trigger a re-fetch of the data.
  5. If the fetchFunction prop changes, the component should re-fetch the data using the new function.
  6. If the fetchFunction throws an error, the component should handle the error and return a suitable fallback value (e.g., null or an error message).

Edge Cases to Consider:

  • What happens if the fetchFunction takes a long time to execute? Should the component display a loading indicator? (Not required for this challenge, but good to think about).
  • How should the cache be cleared when the component is destroyed?
  • What happens if the fetchFunction returns different data on subsequent calls? (Consider immutability and cache invalidation strategies).

Examples

Example 1:

Input:
fetchFunction: () => Promise<string> (returns "Initial Data")
TTL: 2000ms

Initial Render: fetchFunction() is called, returns "Initial Data", cached.
Second Render (within 2000ms): Cached data "Initial Data" is returned. fetchFunction() is NOT called.
Third Render (after 2000ms): fetchFunction() is called, returns "Initial Data", cached.
Fourth Render (within 2000ms): Cached data "Initial Data" is returned. fetchFunction() is NOT called.

Output: "Initial Data" (on all renders within the TTL)

Explanation: The data is fetched initially and cached. Subsequent renders within the TTL return the cached data. After the TTL expires, the data is re-fetched.

Example 2:

Input:
fetchFunction1: () => Promise<string> (returns "Data 1")
TTL: 1000ms

fetchFunction2: () => Promise<string> (returns "Data 2")

Initial Render: fetchFunction1() is called, returns "Data 1", cached.
Second Render (within 1000ms): Cached data "Data 1" is returned. fetchFunction1() is NOT called.
Third Render (after 1000ms, fetchFunction2 is passed): fetchFunction2() is called, returns "Data 2", cached.
Fourth Render (within 1000ms, fetchFunction2 is passed): Cached data "Data 2" is returned. fetchFunction2() is NOT called.

Output: "Data 1" then "Data 2"

Explanation: Changing the fetchFunction prop triggers a re-fetch and updates the cache.

Example 3: (Error Handling)

Input:
fetchFunction: () => Promise<string> (throws an error)
TTL: 5000ms
Fallback Value: "Error fetching data"

Initial Render: fetchFunction() throws an error. "Error fetching data" is returned.
Subsequent Renders: "Error fetching data" is returned.

Output: "Error fetching data"

Explanation: The component handles the error from the fetchFunction and returns the fallback value.

Constraints

  • TTL: The TTL must be configurable via a prop (in milliseconds).
  • fetchFunction: The fetchFunction must be an asynchronous function that returns a Promise.
  • Component Structure: The solution should be a single Vue component.
  • Performance: The cache lookup should be efficient (O(1) or better).
  • TypeScript: Strict TypeScript typing is expected.

Notes

  • Consider using setTimeout or setInterval to manage the cache expiration.
  • Think about how to handle race conditions if the fetchFunction takes a significant amount of time to execute.
  • You can use Vue's reactivity system to automatically update the component when the cached data changes.
  • Focus on clarity, maintainability, and correctness. Don't over-engineer the solution; a simple and effective implementation is preferred.
  • Consider using a Map or Object for the cache storage.
Loading editor...
typescript