- Регистрация
- 1 Мар 2015
- Сообщения
- 1,481
- Баллы
- 155
Infinite scrolling is probably one of the most widely used features in today's web applications. Rather than relying on traditional pagination, it loads further content dynamically when users scroll. In this tutorial, you will learn how to implement a basic and simple Infinite Scroll component in React JS by fetching GitHub users from the GitHub API.?
We will build a clean React application showing GitHub users as cards. When users scroll, additional users will be fetched automatically, providing an overall smooth browsing experience.
? Tech Stack:
useState to manage state
useEffect to handle side effects like API calls and event listeners
? fetch API to load data
? Step-by-Step Implementation
Project Setup and Initial Imports
Ensure your React project is set up.
Import Essentials:
Open InfiniteScroll.jsx file and start with the following imports.
import React, { useEffect, useState } from 'react'
import './InfiniteScroll.css'
In the given code snippet above, we are importing useState and useEffect from React, and we are importing a CSS file to style the user cards later.
States Configuration
We shall manage three primary pieces of state:
? users – stores the fetched users.
? page – track the current page.
? isLoading – prevents multiple simultaneous API calls.
const [users, setUsers] = useState([]);
const [page, setPage] = useState(1);
const [isLoading, setIsLoading] = useState(false);
Fetch Users from GitHub API
We are creating an async function, which allows us to use the await keyword in it. Using async makes it easier to write asynchronous code, which improves readability.
const getUsersFromAPI = async () => {
try {
setIsLoading(true);
const response = await fetch(`{(page - 1) * 30}`);
const data = await response.json();
setUsers((prevUsers) => [...prevUsers, ...data]);
setPage((prevPage) => prevPage + 1);
} catch (error) {
console.error(`Error while fetching users: ${error}`);
} finally {
setIsLoading(false);
}
}
Handle Scroll Events
We will create a function called handleScroll, which checks if the user is near the bottom of the page. If yes, it will invoke the getUsersFromAPI() function to fetch more users.
It also ensures that, as a request is in progress, no new request is sent (isLoading prevents this).
const handleScroll = () => {
if (isLoading) return;
if (window.innerHeight + window.scrollY >= document.body.offsetHeight - 10) {
getUsersFromAPI();
}
}
Setup Lifecycle with useEffect
The useEffect hook runs once when the component is mounted.
useEffect(() => {
getUsersFromAPI();
window.addEventListener('scroll', handleScroll);
return () => {
window.removeEventListener('scroll', handleScroll);
}
}, []);
Render Users in Card Format
We iterate over the users array using the map() function and display each user in a stylish card layout. Each card displays the user's avatar, username, and account type (i.e., User or Organization), and a link to their GitHub profile is displayed when the card is hovered over.
{users.map((user) => (
<div className="card" key={user.id}>
<div className="image-container">
<img src={user.avatar_url} alt={user.login} />
</div>
<div className="content-container">
<div className="contentBx">
<h3>
{user.login}<br />
<span>{user.type}</span>
</h3>
</div>
<div className="sci">
<a href={user.html_url} target='blank' rel='noopener noreferrer'>? View Profile</a>
</div>
</div>
</div>
))}
Complete code snippet
import React, { useEffect, useState } from 'react'
import './InfiniteScroll.css'
const InfiniteScroll = () => {
const [users, setUsers] = useState([]);
const [page, setPage] = useState(1);
const [isLoading, setIsLoading] = useState(false);
// function to fecth users from Github API
const getUsersFromAPI = async () => {
try {
setIsLoading(true);
const response = await fetch(`{(page - 1) * 30}`);
const data = await response.json();
setUsers((prevUsers) => [...prevUsers, ...data]);
setPage((prevPage) => prevPage + 1);
} catch (error) {
console.error(`Error while fetching users: ${error}`);
} finally {
setIsLoading(false);
}
}
// function to check scroll position and load more if needed
const handleScroll = () => {
if (isLoading) return;
if (window.innerHeight + window.scrollY >= document.body.offsetHeight - 10) {
getUsersFromAPI();
}
}
// Initial load and attach scroll listener
useEffect(() => {
getUsersFromAPI();
window.addEventListener('scroll', handleScroll);
return () => {
window.removeEventListener('scroll', handleScroll);
}
}, []);
return (
<>
<h1 className='page-title'>Github Users</h1>
<section>
<div className="container">
{users.map((user) => (
<div className="card" key={user.id}>
<div className="image-container">
<img src={user.avatar_url} alt={user.login} />
</div>
<div className="content-container">
<div className="content">
<h3>
{user.login}<br />
<span>{user.type}</span>
</h3>
</div>
<div className="social">
<a href={user.html_url} target='blank' rel='noopener noreferrer'>View Profile</a>
</div>
</div>
</div>
))}
</div>
</section>
</>
)
}
export default InfiniteScroll
Styling with CSS ??
CSS is essential for creating engaging user interfaces and enhancing the look and feel of your components.
For styling the InfiniteScroll component, simply copy the provided CSS code and paste it into your InfiniteScroll.css file.
Feel free to customize and experiment with the styles to match your app's design preferences. ?️
In this tutorial, I am using nested CSS, a modern and in-demand trend that improves code readability and maintains cleaner structures, especially when working on component-based designs.
However, if you prefer, you can always stick to the traditional flat CSS syntax, which works just as well. The choice depends on your project needs and personal preference.
.page-title {
text-align: center;
margin: 20px;
}
.container {
position: relative;
display: flex;
justify-content: center;
align-items: center;
flex-wrap: wrap;
gap: 20px;
padding: 20px;
.card {
position: relative;
display: flex;
justify-content: center;
align-items: center;
width: 250px;
height: 200px;
overflow: hidden;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
border-radius: 15px;
&:hover .content-container {
bottom: 0;
transition-delay: 0s;
cursor: pointer;
}
&:hover .content-container .content h3 {
opacity: 1;
transform: translateY(0px);
}
&:hover .content-container .social {
transform: translateY(0px);
opacity: 1;
border: 1px solid #867cc1;
padding: 5px;
border-radius: 10px;
}
.image-container {
position: relative;
height: 100%;
width: 100%;
img {
height: 100%;
width: 100%;
object-fit: cover;
}
}
.content-container {
position: absolute;
bottom: -135px;
width: 100%;
height: 125px;
display: flex;
justify-content: center;
align-items: center;
z-index: 10;
flex-direction: column;
backdrop-filter: blur(15px);
box-shadow: 0 -10px 10px rgba(0, 0, 0, 0.1);
border: 1px solid rgba(255, 255, 255, 0.2);
transition: bottom 0.5s;
transition-delay: 0.3s;
.content {
h3 {
color: #fff;
text-transform: capitalize;
letter-spacing: 2px;
font-weight: bold;
font-size: 18px;
text-align: center;
margin: 10px 0 5px;
line-height: 1.1em;
transition: 0.5s;
opacity: 0;
transform: translateY(-20px);
transition-delay: 0.5s;
}
span {
font-size: 12px;
font-weight: 300;
text-transform: initial;
}
}
.social {
position: relative;
bottom: 0x;
display: flex;
transform: translateY(40px);
opacity: 0;
transition-delay: 0.3s;
a {
color: #d3d3d3;
font-size: 15px;
text-decoration: none;
}
}
}
}
}
? Key Learnings
useState – To manage data, page number, and loading state
useEffect – To handle side-effects and lifecycle events like API calls and scroll listeners
fetch API – To make HTTP requests to the GitHub API
map() – To dynamically render user cards
Scroll Event – To detect when the user reaches to the bottom of the page
? Wrapping Up
In this tutorial, we explored how to build a smooth infinite scroll feature in React JS, pulling real-time data from the GitHub API and presenting it in an elegant card layout. ?
The focus was on keeping the code clean and beginner-friendly, sticking to the core React hooks like useState and useEffect without diving into complex patterns.
By following along, you now have a solid understanding of how state management, side effects, and event listeners can work together to create engaging, dynamic user experiences. ?
If you made it this far, I’d love to hear your feedback or if there's anything you think I could have explained better.
Let’s keep learning together! ?
Happy Coding! ?????
?What We Will Build?? Perfect for beginners! This guide focuses on the core logic without overwhelming you with complex React patterns.
We will build a clean React application showing GitHub users as cards. When users scroll, additional users will be fetched automatically, providing an overall smooth browsing experience.
? Tech Stack:
? fetch API to load data
? Step-by-Step Implementation
Ensure your React project is set up.
Make a components directory inside the src folder, and create 2 files in it:? Tools like Vitemake the process super fast.
- InfiniteScroll.jsx
- InfiniteScroll.css
Import Essentials:
Open InfiniteScroll.jsx file and start with the following imports.
import React, { useEffect, useState } from 'react'
import './InfiniteScroll.css'
In the given code snippet above, we are importing useState and useEffect from React, and we are importing a CSS file to style the user cards later.
We shall manage three primary pieces of state:
? users – stores the fetched users.
? page – track the current page.
? isLoading – prevents multiple simultaneous API calls.
const [users, setUsers] = useState([]);
const [page, setPage] = useState(1);
const [isLoading, setIsLoading] = useState(false);
We are creating an async function, which allows us to use the await keyword in it. Using async makes it easier to write asynchronous code, which improves readability.
The getUsersFromAPI() function makes an asynchronous request to the GitHub API to retrieve a list of users, using the current page number to paginate the results.
When the data is successfully received, it adds the new users to the existing list and sets the next page number for retrieval.
If there is any exception in the process, it catches the exception and logs it to the console for debugging.
The function always stops the loading indicator, whether it is successful or not, to update the user interface accordingly.
const getUsersFromAPI = async () => {
try {
setIsLoading(true);
const response = await fetch(`{(page - 1) * 30}`);
const data = await response.json();
setUsers((prevUsers) => [...prevUsers, ...data]);
setPage((prevPage) => prevPage + 1);
} catch (error) {
console.error(`Error while fetching users: ${error}`);
} finally {
setIsLoading(false);
}
}
We will create a function called handleScroll, which checks if the user is near the bottom of the page. If yes, it will invoke the getUsersFromAPI() function to fetch more users.
It also ensures that, as a request is in progress, no new request is sent (isLoading prevents this).
const handleScroll = () => {
if (isLoading) return;
if (window.innerHeight + window.scrollY >= document.body.offsetHeight - 10) {
getUsersFromAPI();
}
}
The useEffect hook runs once when the component is mounted.
- It makes a call to the getUsersFromAPI() method to load the first list of users.
- Adds a scroll event listener.
- Cleans the listener when the component is removed.
useEffect(() => {
getUsersFromAPI();
window.addEventListener('scroll', handleScroll);
return () => {
window.removeEventListener('scroll', handleScroll);
}
}, []);
We iterate over the users array using the map() function and display each user in a stylish card layout. Each card displays the user's avatar, username, and account type (i.e., User or Organization), and a link to their GitHub profile is displayed when the card is hovered over.
{users.map((user) => (
<div className="card" key={user.id}>
<div className="image-container">
<img src={user.avatar_url} alt={user.login} />
</div>
<div className="content-container">
<div className="contentBx">
<h3>
{user.login}<br />
<span>{user.type}</span>
</h3>
</div>
<div className="sci">
<a href={user.html_url} target='blank' rel='noopener noreferrer'>? View Profile</a>
</div>
</div>
</div>
))}
Complete code snippet
import React, { useEffect, useState } from 'react'
import './InfiniteScroll.css'
const InfiniteScroll = () => {
const [users, setUsers] = useState([]);
const [page, setPage] = useState(1);
const [isLoading, setIsLoading] = useState(false);
// function to fecth users from Github API
const getUsersFromAPI = async () => {
try {
setIsLoading(true);
const response = await fetch(`{(page - 1) * 30}`);
const data = await response.json();
setUsers((prevUsers) => [...prevUsers, ...data]);
setPage((prevPage) => prevPage + 1);
} catch (error) {
console.error(`Error while fetching users: ${error}`);
} finally {
setIsLoading(false);
}
}
// function to check scroll position and load more if needed
const handleScroll = () => {
if (isLoading) return;
if (window.innerHeight + window.scrollY >= document.body.offsetHeight - 10) {
getUsersFromAPI();
}
}
// Initial load and attach scroll listener
useEffect(() => {
getUsersFromAPI();
window.addEventListener('scroll', handleScroll);
return () => {
window.removeEventListener('scroll', handleScroll);
}
}, []);
return (
<>
<h1 className='page-title'>Github Users</h1>
<section>
<div className="container">
{users.map((user) => (
<div className="card" key={user.id}>
<div className="image-container">
<img src={user.avatar_url} alt={user.login} />
</div>
<div className="content-container">
<div className="content">
<h3>
{user.login}<br />
<span>{user.type}</span>
</h3>
</div>
<div className="social">
<a href={user.html_url} target='blank' rel='noopener noreferrer'>View Profile</a>
</div>
</div>
</div>
))}
</div>
</section>
</>
)
}
export default InfiniteScroll
CSS is essential for creating engaging user interfaces and enhancing the look and feel of your components.
For styling the InfiniteScroll component, simply copy the provided CSS code and paste it into your InfiniteScroll.css file.
Feel free to customize and experiment with the styles to match your app's design preferences. ?️
However, if you prefer, you can always stick to the traditional flat CSS syntax, which works just as well. The choice depends on your project needs and personal preference.
.page-title {
text-align: center;
margin: 20px;
}
.container {
position: relative;
display: flex;
justify-content: center;
align-items: center;
flex-wrap: wrap;
gap: 20px;
padding: 20px;
.card {
position: relative;
display: flex;
justify-content: center;
align-items: center;
width: 250px;
height: 200px;
overflow: hidden;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
border-radius: 15px;
&:hover .content-container {
bottom: 0;
transition-delay: 0s;
cursor: pointer;
}
&:hover .content-container .content h3 {
opacity: 1;
transform: translateY(0px);
}
&:hover .content-container .social {
transform: translateY(0px);
opacity: 1;
border: 1px solid #867cc1;
padding: 5px;
border-radius: 10px;
}
.image-container {
position: relative;
height: 100%;
width: 100%;
img {
height: 100%;
width: 100%;
object-fit: cover;
}
}
.content-container {
position: absolute;
bottom: -135px;
width: 100%;
height: 125px;
display: flex;
justify-content: center;
align-items: center;
z-index: 10;
flex-direction: column;
backdrop-filter: blur(15px);
box-shadow: 0 -10px 10px rgba(0, 0, 0, 0.1);
border: 1px solid rgba(255, 255, 255, 0.2);
transition: bottom 0.5s;
transition-delay: 0.3s;
.content {
h3 {
color: #fff;
text-transform: capitalize;
letter-spacing: 2px;
font-weight: bold;
font-size: 18px;
text-align: center;
margin: 10px 0 5px;
line-height: 1.1em;
transition: 0.5s;
opacity: 0;
transform: translateY(-20px);
transition-delay: 0.5s;
}
span {
font-size: 12px;
font-weight: 300;
text-transform: initial;
}
}
.social {
position: relative;
bottom: 0x;
display: flex;
transform: translateY(40px);
opacity: 0;
transition-delay: 0.3s;
a {
color: #d3d3d3;
font-size: 15px;
text-decoration: none;
}
}
}
}
}
? Key Learnings
? Wrapping Up
In this tutorial, we explored how to build a smooth infinite scroll feature in React JS, pulling real-time data from the GitHub API and presenting it in an elegant card layout. ?
The focus was on keeping the code clean and beginner-friendly, sticking to the core React hooks like useState and useEffect without diving into complex patterns.
By following along, you now have a solid understanding of how state management, side effects, and event listeners can work together to create engaging, dynamic user experiences. ?
If you made it this far, I’d love to hear your feedback or if there's anything you think I could have explained better.
Let’s keep learning together! ?
Happy Coding! ?????