Hone logo
Hone
Problems

Building Reusable Logic with Vue Composables

This challenge focuses on creating custom Vue.js composables (often referred to as "hooks" in Vue 3 and Composition API) to encapsulate and reuse complex component logic. By abstracting stateful logic into composables, you'll make your Vue components cleaner, more organized, and easier to maintain.

Problem Description

You are tasked with building a set of reusable composables for a common UI pattern: managing and displaying a list of items with loading and error states. Specifically, you need to create two composables:

  1. useFetch: This composable will handle fetching data from a given URL, managing its loading state, and catching any potential errors during the fetch process.
  2. usePaginatedList: This composable will take the data fetched by useFetch and provide functionality for pagination (e.g., displaying a subset of items, navigating between pages).

You will then need to demonstrate how these composables can be used together in a Vue component to display a paginated list of users fetched from a public API.

Key Requirements:

  • useFetch Composable:
    • Accepts a URL string as an argument.
    • Exposes data (the fetched response), isLoading (a boolean indicating if a fetch is in progress), and error (an error object if the fetch fails) as reactive properties.
    • Initiates the fetch operation automatically when the composable is used.
    • Should handle potential network errors.
  • usePaginatedList Composable:
    • Accepts the data from useFetch as an argument.
    • Accepts an optional itemsPerPage argument (defaulting to 10).
    • Exposes currentPage (the current page number), totalPages (the total number of pages), paginatedItems (the array of items for the current page), and methods like nextPage() and prevPage().
    • Recalculates totalPages and paginatedItems whenever the input data or itemsPerPage changes.
  • Integration:
    • Create a Vue component that uses both useFetch and usePaginatedList to display a list of users from https://jsonplaceholder.typicode.com/users.
    • The component should display the user list, pagination controls (previous/next buttons), the current page number, and total pages.
    • It should also display a loading indicator when data is being fetched and an error message if the fetch fails.

Expected Behavior:

  • When the component mounts, the useFetch composable should start fetching user data.
  • While fetching, a "Loading..." message should be displayed.
  • If the fetch is successful, the user data will be passed to usePaginatedList.
  • The first page of users (up to itemsPerPage) will be displayed.
  • Pagination controls should be enabled/disabled appropriately (e.g., "Previous" button disabled on the first page, "Next" button disabled on the last page).
  • Clicking "Next" should display the next set of users and update the page state.
  • Clicking "Previous" should display the previous set of users and update the page state.
  • If an error occurs during fetching, an error message should be displayed.

Edge Cases:

  • What happens if the API returns an empty array?
  • What happens if itemsPerPage is set to 0 or a negative number? (Consider adding validation or handling this gracefully).

Examples

Example 1: useFetch Success

Input to useFetch: "https://jsonplaceholder.typicode.com/users"

Output from useFetch:

  • data: Array of user objects (e.g., [{ id: 1, name: 'Leanne Graham', ... }, ...])
  • isLoading: false
  • error: null

Explanation: The fetch operation completed successfully, and the API response is available in data.

Example 2: useFetch Loading

Input to useFetch: "https://jsonplaceholder.typicode.com/users"

Output from useFetch during fetch:

  • data: null (or initial value)
  • isLoading: true
  • error: null

Explanation: The fetch operation is currently in progress.

Example 3: useFetch Error

Input to useFetch: "https://invalid.url.com/users"

Output from useFetch on error:

  • data: null (or initial value)
  • isLoading: false
  • error: new Error('Failed to fetch') (or similar)

Explanation: The fetch operation failed due to an invalid URL or network issue.

Example 4: usePaginatedList with data

Input to usePaginatedList:

  • data: [user1, user2, ..., user15]
  • itemsPerPage: 5

Output from usePaginatedList (when currentPage is 1):

  • currentPage: 1
  • totalPages: 3
  • paginatedItems: [user1, user2, user3, user4, user5]
  • nextPage(): Function to increment currentPage
  • prevPage(): Function to decrement currentPage

Explanation: The list is divided into pages of 5 items. The first page displays the first 5 users.

Constraints

  • You must use Vue 3 and the Composition API.
  • The useFetch composable should use the native fetch API.
  • The useFetch composable should be called automatically when it's first used within a component setup.
  • The usePaginatedList composable should correctly handle the recalculation of pagination when the input data changes.
  • The final Vue component should be a single-file component (.vue file).

Notes

  • Consider using ref and computed from Vue's reactivity API within your composables.
  • Think about how to handle the initial state of data before the fetch completes.
  • For useFetch, ensure you handle potential CORS issues if you were to test with a different API that doesn't allow cross-origin requests from your local development server. The provided jsonplaceholder API generally works without issue.
  • The usePaginatedList composable should be designed to be generic and reusable for any array of data, not just users.
Loading editor...
javascript