Machine Coding
Infinite Scroll
Master building an Infinite Scroll component in React, covering Intersection Observer API, scroll-based data fetching, loading indicators, and scroll performance optimizations.
1. Learning Objectives
In this challenge, you will build an Infinite Scroll component. By the end of this guide, you will be able to:
- Use the
IntersectionObserverAPI to detect when a sentinel element enters the viewport. - Fetch and append paginated data when the user scrolls to the bottom.
- Manage loading states, error handling, and "no more data" indicators.
- Clean up observers properly using
useEffectreturn functions. - Prevent duplicate fetch requests using loading guards.
2. Overview
Infinite Scroll is a UI pattern that loads content continuously as the user scrolls, replacing traditional pagination buttons. It is commonly tested in machine coding interviews because it involves API integration, browser APIs, ref management, and performance considerations.
3. Why This Challenge Matters
Infinite scroll teaches critical patterns for production applications:
- Browser API Integration: Using
IntersectionObserveris more performant than scroll event listeners and teaches you how to integrate browser APIs with React's lifecycle. - Paginated Data Management: Appending new data to existing lists without replacing them is a pattern used in social feeds, search results, and product catalogs.
4. Real-World Analogy
Think of infinite scroll like reading a newspaper on a microfiche machine:
- The Viewport: The machine's viewing window shows only a small portion of the film at a time — this is the visible part of your scrollable list.
- The Sentinel: A small mark at the bottom of each frame tells the machine to advance to the next reel — this is the invisible sentinel element observed by
IntersectionObserver. - Loading the Next Reel: When the mark enters the viewport, the machine loads the next reel (fetches the next page of data) and seamlessly continues.
5. Core Concepts
Below is a comparison of scroll event listeners versus Intersection Observer:
| Property | Scroll Event Listener | Intersection Observer |
|---|---|---|
| Performance | Fires on every pixel scrolled; requires throttling or debouncing. | Fires only when an element enters or exits the viewport; inherently efficient. |
| Setup | Manual addEventListener and manual calculation of scroll positions. |
Declarative: create an observer, point it at a DOM element, and define a callback. |
| Cleanup | Must removeEventListener in useEffect cleanup. |
Call observer.disconnect() in useEffect cleanup. |
| Recommended | Legacy approach | Modern recommended approach |
6. Syntax & API Reference
This example shows how to set up an Intersection Observer with a React ref:
7. Visual Diagram
This diagram shows the infinite scroll fetch cycle:
8. Live Example — Full Working Code
Below is a complete HTML page demonstrating infinite scroll with a mock API using setTimeout:
What just happened? A sentinel <div> sits at the bottom of the list. When it enters the viewport, the IntersectionObserver fires, triggering the next page fetch. New items are appended to the existing array, pushing the sentinel further down for the next trigger.
9. Interactive Playground
Try It Yourself Challenges:
- Add error handling: simulate a network failure on page 3 and display a "Retry" button.
- Add a "scroll to top" floating button that appears when the user has scrolled past 20 items.
10. Common Mistakes
| Mistake | Why it happens | Wrong | Correct |
|---|---|---|---|
| Duplicate fetch requests | The observer callback fires multiple times while data is still loading, triggering concurrent fetches for the same page. | if (entries[0].isIntersecting) |
if (entries[0].isIntersecting |
| Replacing items instead of appending | Using setItems(data) replaces the entire list with just the new page data, losing all previously loaded items. |
setItems(newData); |
setItems(prev => |
11. Best Practices
- Use IntersectionObserver over scroll events: It is more performant and doesn't require manual throttling or debouncing.
- Guard against duplicate fetches: Always check that
loadingis false andhasMoreis true before initiating a fetch. - Disconnect observers on cleanup: Always disconnect or unobserve in the
useEffectcleanup function to prevent memory leaks. - Show end-of-list indicators: Let users know when there is no more content to load, instead of showing an infinite loading spinner.
12. Browser Compatibility/Requirements
The IntersectionObserver API is supported in all modern browsers (Chrome 58+, Firefox 55+, Safari 12.1+, Edge 16+). For older browsers, you can use the intersection-observer polyfill.
13. Interview Questions
Q1: Why is IntersectionObserver preferred over scroll event listeners for infinite scroll?
Answer:
Scroll event listeners fire on every pixel scrolled, which is very frequent and can cause performance issues (layout thrashing). IntersectionObserver only fires when the observed element enters or exits the viewport, making it inherently more efficient without needing manual throttling.
Q2: How do you prevent duplicate API calls in infinite scroll?
Answer: Maintain a loading boolean state variable. Set it to true before the fetch starts and false after it completes. In the observer callback, check that loading is false before triggering a new fetch. Also check a hasMore flag to stop fetching when all data has been loaded.
14. Debugging Exercise
Identify why the infinite scroll fetches the same page repeatedly without advancing:
Diagnosis: The page state variable is never incremented after a successful fetch. Every call fetches page 1. Additionally, setItems([...items, ...data]) uses the stale items closure value instead of the functional updater.
Fix: Increment the page and use the functional updater for items:
15. Practice Exercises
Exercise 1: Bidirectional infinite scroll
Implement a chat-style infinite scroll that loads older messages when the user scrolls to the top and newer messages when they scroll to the bottom. Use two sentinel elements (one at the top, one at the bottom) with separate observers.
16. Scenario-Based Challenge
The virtualized infinite scroll challenge:
Your infinite scroll feed has grown to 10,000+ items. The page becomes sluggish because all 10,000 DOM elements exist simultaneously. Describe how you would combine infinite scroll with list virtualization (rendering only visible items) to maintain performance while still supporting unlimited scrolling.
17. Quick Quiz
Q1: What does the threshold option in IntersectionObserver control?
A) The distance from the viewport edge at which the callback fires
B) The percentage of the target element that must be visible before the callback fires
C) The maximum number of times the callback can fire
Answer: B — A threshold of 0.5 means the callback fires when 50% of the element is visible. A threshold of 1.0 means the entire element must be visible.
18. Summary & Key Takeaways
- Use
IntersectionObserverto detect when a sentinel element enters the viewport. - Append new data to existing items using the functional state updater:
setItems(prev => [...prev, ...newData]). - Guard against duplicate fetches with
loadingandhasMoreflags. - Always disconnect observers in useEffect cleanup functions.
19. Cheat Sheet
| Operation | Syntax Pattern |
|---|---|
| Create observer | new IntersectionObserver(callback, { threshold: 1.0 }) |
| Observe element | observer.observe(sentinelRef.current) |
| Cleanup | return () => observer.disconnect() |
| Append data | setItems(prev => [...prev, ...newData]) |