UseBuildStepEntityFetch: Streamline Wizard Data Loading

by Alex Johnson 56 views

In modern web application development, efficient data loading is paramount for a smooth user experience. Wizards, with their step-by-step approach, often require fetching data for each step. This article delves into the creation of a useBuildStepEntityFetch composable, designed to streamline data loading within a wizard context, specifically addressing the challenges outlined in the Character Wizard Refactoring project.

The Challenge: Redundant Data Fetching Patterns

Character Wizard Refactoring, as detailed in Parent Epic #147, aims to enhance the structure and maintainability of the character creation wizard. A key issue identified was the repetitive nature of data fetching across different wizard steps. Components like StepRace, StepClass, and StepBackground exhibited similar patterns, involving the use of useAsyncData to retrieve data based on a source filter. Let's examine the problematic code snippet:

const { data, pending } = await useAsyncData(
  `builder-{entity}-${sourceFilterString.value}`,
  () => {
    const filter = sourceFilterString.value
    const url = filter ? `/{entity}?filter=${filter}` : '/{entity}'
    return apiFetch(url)
  },
  { transform: r => r.data, watch: [sourceFilterString] }
)

This code, while functional, presents several drawbacks. Firstly, the repetition violates the DRY (Don't Repeat Yourself) principle, making the codebase harder to maintain. Secondly, the string interpolation for the cache key (builder-{entity}-${sourceFilterString.value}) is prone to errors and lacks flexibility. Thirdly, the manual construction of the URL with the filter parameter is verbose and could be simplified. The goal is to encapsulate this logic into a reusable composable, promoting code reuse and reducing the potential for errors.

The Solution: A Reusable useBuildStepEntityFetch Composable

To address the challenges outlined above, a reusable useBuildStepEntityFetch<T>() composable is proposed. This composable, residing in app/composables/useBuildStepEntityFetch.ts, aims to abstract the data fetching logic common to wizard steps. The composable should be generic, allowing it to be used with different entity types. It should accept the entity endpoint and an optional configuration object, providing flexibility for customization. Crucially, it should automatically combine the source filter with any additional filters provided in the configuration. Finally, it should return an object containing the fetched data, a pending state indicator, and a refresh function.

Key Features of the Composable

  • Generic Type Parameter: The <T> allows the composable to be used with various entity types, ensuring type safety and flexibility.
  • Entity Endpoint and Configuration: Accepting the entity endpoint as a parameter allows the composable to fetch data from different resources. The optional configuration object provides a mechanism for customizing the fetching behavior.
  • Automatic Filter Combination: The composable should intelligently combine the source filter with any additional filters provided in the configuration, simplifying the filtering process for the consumer.
  • Return Value: The composable should return an object containing:
    • data: The fetched data, typed as T.
    • pending: A boolean indicating whether the data is currently being fetched.
    • refresh: A function to manually trigger a data refresh.

Implementation Details and Acceptance Criteria

The implementation of useBuildStepEntityFetch should adhere to the following guidelines:

  1. Composable Creation: Create the useBuildStepEntityFetch<T>() composable in app/composables/useBuildStepEntityFetch.ts.
  2. Generic Type Parameter: Ensure the composable accepts a generic type parameter T.
  3. Parameter Acceptance: The composable should accept the entity endpoint (e.g., /races, /classes) and an optional configuration object.
  4. Filter Combination: Implement the logic to automatically combine the source filter with any additional filters provided in the configuration.
  5. Return Value: The composable should return an object with the structure { data, pending, refresh }.
  6. Unit Tests: Write comprehensive unit tests in tests/composables/useBuildStepEntityFetch.test.ts to ensure the composable functions correctly under various scenarios.

Testing Scenarios

The unit tests should cover the following scenarios:

  • Fetching data without a source filter.
  • Fetching data with a source filter.
  • Fetching data with additional filters in the configuration.
  • Verifying the pending state is correctly updated during the fetching process.
  • Ensuring the refresh function triggers a data refresh.
  • Handling errors during data fetching.

Benefits of Using useBuildStepEntityFetch

The useBuildStepEntityFetch composable offers several advantages:

  • Reduced Code Duplication: By encapsulating the data fetching logic, the composable eliminates redundant code across wizard steps.
  • Improved Maintainability: Changes to the data fetching logic only need to be made in one place, simplifying maintenance and reducing the risk of errors.
  • Increased Readability: The composable makes the code more readable by abstracting away the complex data fetching details.
  • Enhanced Testability: The composable can be easily tested in isolation, ensuring its correctness and reliability.
  • Streamlined Development: Developers can quickly and easily fetch data for wizard steps without having to write repetitive code.

Example Usage

Here's an example of how to use the useBuildStepEntityFetch composable in a wizard step component:

import { useBuildStepEntityFetch } from '~/composables/useBuildStepEntityFetch'

export default defineComponent({
  setup() {
    const { data: races, pending: racesPending, refresh: refreshRaces } = useBuildStepEntityFetch<Race[]>('/races')

    return {
      races,
      racesPending,
      refreshRaces,
    }
  },
})

In this example, the useBuildStepEntityFetch composable is used to fetch a list of races from the /races endpoint. The composable returns the fetched data (races), a pending state indicator (racesPending), and a refresh function (refreshRaces).

Conclusion

The useBuildStepEntityFetch composable provides a robust and reusable solution for streamlining data loading within wizard steps. By encapsulating the common data fetching logic, it reduces code duplication, improves maintainability, and enhances the overall development experience. This composable is a valuable asset for any project involving wizards or step-by-step data entry processes. The effort invested in creating such composables not only addresses immediate needs but also contributes to a more maintainable and scalable codebase in the long run. Embracing such practices is crucial for fostering efficient and reliable web application development.

Vue.js Composition API Documentation