Hone logo
Hone
Problems

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 data prop (an array of any type – use T[] for generic typing).
  • The component should accept a fetchNextChunk prop, which is a function that takes a startIndex (number) as an argument and returns a Promise resolving to an array of data items (of type T[]). This function simulates fetching data from an API.
  • The component should accept a chunkSize prop (number, default 20) to control how many items are loaded at a time.
  • The component should render a loading indicator while fetchNextChunk is executing.
  • The component should disable the loading of further chunks when all data has been fetched (i.e., fetchNextChunk returns 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:

  1. Initially, the component renders the first chunkSize items from the data array.
  2. As the user scrolls near the bottom of the list (e.g., within the last 10% of the list height), the component should call fetchNextChunk with the appropriate startIndex.
  3. While fetchNextChunk is executing, a loading indicator should be displayed.
  4. Upon successful data fetching, the new chunk of items should be appended to the list.
  5. If fetchNextChunk returns an empty array or signals the end of data, the loading indicator should disappear, and further loading should be disabled.
  6. If fetchNextChunk throws an error, an error message should be displayed.

Edge Cases to Consider:

  • Empty initial data array.
  • fetchNextChunk returning an empty array immediately.
  • fetchNextChunk throwing an error.
  • Very large data array.
  • chunkSize being 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

  • chunkSize must be a positive integer.
  • startIndex passed to fetchNextChunk must be a non-negative integer.
  • The component should be reasonably performant, avoiding unnecessary re-renders. Use useMemo and useCallback where appropriate.
  • The component should be responsive and adapt to different screen sizes.

Notes

  • Consider using the IntersectionObserver API 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 fetchNextChunk function 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 fetchNextChunk function is a placeholder.
Loading editor...
typescript