React Resumeable List Component
This challenge focuses on building a React component that displays a list of items in a resumeable fashion. Imagine a very long list (e.g., search results, a large database of products) that would be slow to render entirely at once. This component will load and render items in chunks, allowing the user to scroll and progressively load more data, improving performance and user experience.
Problem Description
You are tasked with creating a ResumeableList component in React using TypeScript. This component should accept an array of data items and a function to fetch the next chunk of data when the user scrolls near the end of the currently rendered list. The component should initially render a limited number of items and provide a mechanism to load more as the user scrolls.
What needs to be achieved:
- Create a React component that renders a list of items.
- Implement a mechanism to load data in chunks (e.g., 20 items at a time).
- Provide a loading indicator while fetching the next chunk of data.
- Disable further loading when all data has been fetched.
Key Requirements:
- The component should accept a
dataprop (an array of any type – useT[]for generic typing). - The component should accept a
fetchNextChunkprop, which is a function that takes astartIndex(number) as an argument and returns a Promise resolving to an array of data items (of typeT[]). This function simulates fetching data from an API. - The component should accept a
chunkSizeprop (number, default 20) to control how many items are loaded at a time. - The component should render a loading indicator while
fetchNextChunkis executing. - The component should disable the loading of further chunks when all data has been fetched (i.e.,
fetchNextChunkreturns an empty array or signals the end of data). - The component should handle potential errors during data fetching (e.g., display an error message).
Expected Behavior:
- Initially, the component renders the first
chunkSizeitems from thedataarray. - As the user scrolls near the bottom of the list (e.g., within the last 10% of the list height), the component should call
fetchNextChunkwith the appropriatestartIndex. - While
fetchNextChunkis executing, a loading indicator should be displayed. - Upon successful data fetching, the new chunk of items should be appended to the list.
- If
fetchNextChunkreturns an empty array or signals the end of data, the loading indicator should disappear, and further loading should be disabled. - If
fetchNextChunkthrows an error, an error message should be displayed.
Edge Cases to Consider:
- Empty initial
dataarray. fetchNextChunkreturning an empty array immediately.fetchNextChunkthrowing an error.- Very large
dataarray. chunkSizebeing larger than the remaining data.
Examples
Example 1:
Input: data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22], fetchNextChunk = (startIndex: number) => Promise.resolve(data.slice(startIndex, startIndex + 20)), chunkSize = 10
Output: Initially renders [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]. On scroll, renders [11, 12, 13, 14, 15, 16, 17, 18, 19, 20]. On scroll, renders [21, 22]. Further scrolling does nothing.
Explanation: The component loads data in chunks of 10. After rendering the first 10 items, it calls fetchNextChunk with startIndex 10, and so on.
Example 2:
Input: data = [1, 2, 3], fetchNextChunk = (startIndex: number) => Promise.resolve([]), chunkSize = 2
Output: Renders [1, 2]. The loading indicator appears briefly, then disappears. Further scrolling does nothing.
Explanation: The component loads the first 2 items. fetchNextChunk returns an empty array, signaling the end of data.
Example 3:
Input: data = [1, 2, 3, 4, 5], fetchNextChunk = (startIndex: number) => Promise.reject("Failed to fetch data"), chunkSize = 2
Output: Renders [1, 2]. An error message "Failed to fetch data" is displayed. Further scrolling does nothing.
Explanation: The component loads the first 2 items. fetchNextChunk throws an error, which is displayed to the user.
Constraints
chunkSizemust be a positive integer.startIndexpassed tofetchNextChunkmust be a non-negative integer.- The component should be reasonably performant, avoiding unnecessary re-renders. Use
useMemoanduseCallbackwhere appropriate. - The component should be responsive and adapt to different screen sizes.
Notes
- Consider using the
IntersectionObserverAPI to detect when the user is near the bottom of the list. This is more efficient than constantly checking the scroll position. - Think about how to handle loading state and error states gracefully.
- Focus on creating a reusable and well-structured component.
- The
fetchNextChunkfunction is assumed to be asynchronous (returns a Promise). - You don't need to implement the actual data fetching logic; just focus on the resumeable list component's behavior. The provided
fetchNextChunkfunction is a placeholder.