Hone logo
Hone
Problems

Implementing Vue 3 Suspense with Asynchronous Components

This challenge focuses on understanding and implementing Vue 3's Suspense component, a powerful feature for managing asynchronous operations in your application. You'll create a scenario where components load asynchronously and leverage Suspense to gracefully handle loading states, providing a better user experience by preventing flickering and showing fallback content.

Problem Description

Your task is to build a simple Vue 3 application that utilizes the <Suspense> component to manage the loading of asynchronous components. You will create two child components: one that simulates a time-consuming data fetch and another that serves as a fallback UI. The parent component will wrap these children within <Suspense> to control their rendering.

Key Requirements:

  1. Asynchronous Child Component: Create a component (e.g., AsyncDataFetcher.vue) that simulates fetching data using setTimeout. This component should be defined using defineAsyncComponent.
  2. Fallback Component: Create a simple component (e.g., LoadingSpinner.vue) that displays a loading indicator.
  3. Suspense Integration: In a parent component (e.g., App.vue), wrap the AsyncDataFetcher component within <Suspense>.
  4. Fallback Slot: Use the #fallback slot within <Suspense> to render the LoadingSpinner component while AsyncDataFetcher is loading.
  5. Successful Render: Once AsyncDataFetcher has completed its "fetch" and is ready, it should be rendered in place of the fallback.
  6. Error Handling (Optional but Recommended): Consider how you might handle potential errors during the asynchronous fetch. While not strictly required for the core Suspense functionality, it's a good practice.

Expected Behavior:

  • When the App.vue component mounts, the LoadingSpinner should be visible immediately.
  • After a short delay (simulating data fetching), the AsyncDataFetcher component's content should replace the LoadingSpinner.
  • The application should not show any raw HTML or uninitialized states while the asynchronous component is loading.

Edge Cases:

  • What happens if the asynchronous component fails to load (e.g., throws an error)? (This is an optional extension, but worth considering.)

Examples

Example 1:

App.vue (Parent Component Structure):

<template>
  <div>
    <h1>My App</h1>
    <Suspense>
      <template #default>
        <AsyncDataFetcher />
      </template>
      <template #fallback>
        <LoadingSpinner />
      </template>
    </Suspense>
  </div>
</template>

<script lang="ts">
import { defineComponent, defineAsyncComponent } from 'vue';

const AsyncDataFetcher = defineAsyncComponent(() =>
  new Promise((resolve) => {
    setTimeout(() => {
      resolve({
        template: '<div>Data loaded successfully!</div>',
      });
    }, 2000); // Simulate a 2-second fetch
  })
);

const LoadingSpinner = {
  template: '<div>Loading...</div>',
};

export default defineComponent({
  components: {
    AsyncDataFetcher,
    LoadingSpinner,
  },
});
</script>

Expected Output (after 2 seconds):

<div>
  <h1>My App</h1>
  <div>Data loaded successfully!</div>
</div>

Explanation:

Initially, <Suspense> renders the #fallback content (LoadingSpinner). After 2 seconds, the AsyncDataFetcher component resolves, and <Suspense> replaces the fallback with the AsyncDataFetcher's content.

Example 2: (Illustrating the initial state)

App.vue (Same as Example 1)

Expected Output (immediately after mount, before 2-second delay):

<div>
  <h1>My App</h1>
  <div>Loading...</div>
</div>

Explanation:

This shows the initial state where the LoadingSpinner is displayed because the AsyncDataFetcher is still being loaded.

Constraints

  • The solution must be implemented using Vue 3 and TypeScript.
  • The asynchronous component must be defined using defineAsyncComponent.
  • The core functionality should demonstrate the use of the <Suspense> component with both a default slot and a fallback slot.
  • The simulated data fetch in the asynchronous component should have a duration of at least 1000ms (1 second).

Notes

  • defineAsyncComponent is the key to creating components that can be loaded on demand.
  • The <Suspense> component works by default with any component defined using defineAsyncComponent that returns a Promise.
  • When the Promise resolves, the component in the #default slot will be rendered.
  • If the Promise rejects, the error will be caught by Vue's error handling mechanisms (or you can implement custom error handling with Suspense).
  • For this challenge, focus on the successful loading scenario. Error handling can be an extension.
Loading editor...
typescript