Introduction
Have you ever noticed how apps like Instagram or Twitter show fresh content as you scroll? That’s because of a clever feature called infinite scrolling. It lets you keep browsing without having to click through multiple pages or reload.
Instead of a “Next Page” button, infinite scrolling automatically loads more data as the user scrolls, which creates a smoother, more efficient experience. It’s now used in a lot of apps, from social media to news sites and even online stores.
In this blog, we’ll explore how to build a similar feature with JavaScript. Using the Intersection Observer API, we’ll create a React app that loads posts dynamically as you scroll, just like the content-heavy apps we see today.
Prerequisites
- Node.js and npm
- React
- MUI
How the Intersection Observer API Works
The Intersection Observer API is a tool that lets us track when an element enters or leaves the viewport. Continuously checking the scroll position is not feasible because it can lead to performance issues. Frequent scroll event listeners trigger multiple updates per second, causing unnecessary re-renders and excessive computations, which can degrade the user experience. So, instead of checking the scroll position continuously, we can set it up to watch specific elements and run actions when they appear on the screen. In the case of infinite scrolling, it helps us load more content when the last item is visible.
This method is more efficient than older scroll event listeners because it offloads the work to the browser, avoiding performance problems and making the app run smoother.
Create a React application
Start the React App
If you don’t have a React project yet, you can make one like this:
//Create react app using the following command
npx create-react-app infinite-scrolling
//Install the required library
npm i @mui/material @emotion/react @emotion/styled
Let’s dive right into the code. We’ll create a React-based app that fetches characters from the Star Wars API (SWAPI) as you scroll. The Star Wars API (SWAPI) is a free, open-source API that provides data from the Star Wars universe, including characters, films, planets, species, and more.
API Endpoint: https://swapi.dev/api
//App.js
//Necessary Imports
const App = () => {
const [characters, setCharacters] = useState([]);
const [page, setPage] = useState(1);
const [hasMore, setHasMore] = useState(true);
const [loading, setLoading] = useState(false);
const observer = useRef(null);
// Function to fetch characters from SWAPI
const fetchCharacters = async () => {
if (loading) return;
setLoading(true);
try {
const response = await fetch(`https://swapi.dev/api/people/?page=${page}`);
const data = await response.json();
if (data?.results?.length) {
// If there is data, append new results to the existing list
setCharacters((prev) => [...prev, ...data.results]);
} else {
setHasMore(false);
}
} catch (error) {
console.error("Error fetching data:", error);
setHasMore(false);
} finally {
setLoading(false);
}
};
useEffect(() => {
fetchCharacters();
}, [page]);
// useCallback to create a ref for the last item
const lastPostElementRef = useCallback(
(node) => {
if (loading) return;
if (observer.current) observer.current.disconnect();
observer.current = new IntersectionObserver((entries) => {
if (entries[0].isIntersecting && hasMore && !loading) {
setPage((prevPage) => prevPage + 1);
}
});
if (node) observer.current.observe(node);
},
[loading, hasMore]
);
return (
Star Wars Characters
{characters.map((character, index) => (
{character.name}
Height: {character.height} cm
Birth Year: {character.birth_year}
Gender: {character.gender}
))}
{loading && hasMore ? : No more characters to load }
);
};
export default App;
Explanation of the Code
1. State Setup
In the beginning, we set up the following state variables using React’s useState hook:
- const [characters, setCharacters] = useState([]): This stores the fetched characters.
- page: This keeps track of the current page for fetching more data.
- hasMore: This tells us if there are more characters to fetch from the API.
- loading: This indicates whether data is being fetched.
2. Fetching Data
The fetchCharacters function is responsible for making an API call to the Star Wars API (SWAPI) to fetch characters. It uses the current page value to fetch the right set of data.
3. Using the Intersection Observer API
To implement infinite scrolling, we use the Intersection Observer API to detect when the user reaches the bottom of the page. The useCallback hook is used to create a reference (lastPostElementRef) to the last item in the list. This reference is then observed, and when the item becomes visible, it triggers the loading of more content.
Here’s how it works:
- Creating the lastPostElementRef reference : We create a reference to the last item in the list and attach it using the useCallback hook. This reference is observed by the IntersectionObserver.
- Assigning the reference : The ref={lastPostElementRef} is assigned to the last element or loading indicator in the list.
When the last item comes into view, it triggers the loading of the next page, creating a seamless infinite scrolling experience.
4. Rendering the Characters
We render the fetched characters inside Material UI’s Card components, displayed in a grid layout. The lastPostElementRef is applied to the last item to detect when it’s in the viewport, triggering the loading of more data.
5. Loading Indicator
We display a loading spinner (CircularProgress) when new data is being fetched. If no more characters are available, we display a message saying “No more characters to load.”
Output
Conclusion
To sum up, infinite scrolling makes browsing smoother by loading content as you scroll. With the Intersection Observer API in React, we’ve built a simple way to load data dynamically, just like in popular apps.