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:
useFetch: This composable will handle fetching data from a given URL, managing its loading state, and catching any potential errors during the fetch process.usePaginatedList: This composable will take the data fetched byuseFetchand 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:
useFetchComposable:- Accepts a URL string as an argument.
- Exposes
data(the fetched response),isLoading(a boolean indicating if a fetch is in progress), anderror(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.
usePaginatedListComposable:- Accepts the
datafromuseFetchas an argument. - Accepts an optional
itemsPerPageargument (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 likenextPage()andprevPage(). - Recalculates
totalPagesandpaginatedItemswhenever the inputdataoritemsPerPagechanges.
- Accepts the
- Integration:
- Create a Vue component that uses both
useFetchandusePaginatedListto display a list of users fromhttps://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.
- Create a Vue component that uses both
Expected Behavior:
- When the component mounts, the
useFetchcomposable 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
itemsPerPageis 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:falseerror: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:trueerror: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:falseerror: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:1totalPages:3paginatedItems:[user1, user2, user3, user4, user5]nextPage(): Function to incrementcurrentPageprevPage(): Function to decrementcurrentPage
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
useFetchcomposable should use the nativefetchAPI. - The
useFetchcomposable should be called automatically when it's first used within a component setup. - The
usePaginatedListcomposable should correctly handle the recalculation of pagination when the input data changes. - The final Vue component should be a single-file component (
.vuefile).
Notes
- Consider using
refandcomputedfrom Vue's reactivity API within your composables. - Think about how to handle the initial state of
databefore 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 providedjsonplaceholderAPI generally works without issue. - The
usePaginatedListcomposable should be designed to be generic and reusable for any array of data, not just users.