We see infinite scrolling in applications and web pages especially social media that want us just to scroll. While mindlessly scrolling is not good, building your own infinite scroll is awesome. As a developer, we should try to recreate components that we see while surfing the web. It can challenge you to learn more and think out of the box when implementing some components.
Also, If you are looking to implement an infinite scroll in your application then you can follow the guide to create your own. You can add your own code to improve the behavior of the scroll.
In this article, we are going to build an infinite scroll component from scratch. It will cover the following topics:
Environment Setup
Building the Component
Adding CSS
Optimizing the Infinite Scroll
Now, let’s get started.
If you wish to Vite or NextJS then you can also. Apart from minor changes other things will remain the same.
Note: To run this command, you need to have NodeJS pre-installed. Also, remove some of the unnecessary boilerplate code from CRA.
We are going to need one dependency to fetch data from an API. After setting React, we can install Axios with the following command:
We are going to build an component that is going to fetch popular movie data from
Tmdb API
. It’s free you can get their API key from their website. Let’s build first where they are fetching the data and then add infinite scrolling features.
Here is the code for the App Component:
App.js
import
"
./App.css
"
;
import
{
useState
,
useEffect
}
from
"
react
"
;
import
axios
from
"
axios
"
;
import
{
MovieCard
}
from
"
./MovieCard
"
;
function
App
()
{
const
[
page
,
setPage
]
=
useState
(
1
);
// for number of page in tmdb
const
[
data
,
setData
]
=
useState
([]);
// storing the fetched data
const
[
loading
,
setLoading
]
=
useState
(
false
);
// for setting loading state
// fetching and stroring the data in the state
const
fetchMovie
=
async
()
=>
{
const
URL
=
`https://api.themoviedb.org/3/movie/popular?language=en-US&page=
${
page
}
`
;
const
data
=
await
axios
.
get
(
URL
,
{
headers
:
{
Authorization
:
"
Bearer API KEY
"
,
Accept
:
"
application/json
"
,
setData
((
prevData
)
=>
[...
prevData
,
...
data
.
data
.
results
]);
// we are going to add the new data to current data.
setLoading
(
false
);
// useEffecte for invoking the function at the start
useEffect
(()
=>
{
fetchMovie
();
},
[
page
]);
return
(
<
div
className
=
"App"
>
<
header
className
=
"App-header"
>
Popular movies according to Tmdb
<
div
className
=
"movieCardContainer"
>
{
data
.
length
>
1
&&
data
.
map
((
item
)
=>
{
return
(
<
MovieCard
key
=
{
item
.
id
}
title
=
{
item
.
original_title
}
description
=
{
item
.
overview
}
rating
=
{
item
.
vote_average
}
imageURL
=
{
item
.
poster_path
}
{
loading
&&
<
h1
>
Loading....
</
h1
>
}
</
div
>
</
header
>
</
div
>
export
default
App
;
You can pretty much understand the code, where we are fetching the data and passing it into the MovieCard component as a prop.
Create a MovieCard.js component for displaying each movie's info.
MoveCard.js
import
React
from
"
react
"
;
export
const
MovieCard
=
({
title
,
description
,
imageURL
,
rating
})
=>
{
const
imagePath
=
`https://image.tmdb.org/t/p/w500
${
imageURL
}
`
;
// poster image path URL
return
(
<
div
className
=
"movieCard"
>
<
img
src
=
{
imagePath
}
height
=
{
400
}
/>
<
div
className
=
"movieInfo"
>
<
h3
>
{
title
}
</
h3
>
<
p
>
{
description
}
</
p
>
<
p
>
{
rating
.
toFixed
(
1
)
}
⭐
</
p
>
</
div
>
</
div
>
Now, let’s first, understand how we are going to build the infinite scroll. For this, we are going to look at the scroll bar position. When the scroll bar position is just above the end of the page, we are going to set the
loading
state to true.
We are going to have another
useEffect
that is going to increment the
page
state by 1. Once the page number is updated, the initial useEffect that has the page as a dependency will trigger. This will invoke the
fetchMovie()
function to fetch the data.
Adding EventListner to Scroll
First, we are going to add even listen to know when the scroll bar position is changed.
window
.
addEventListener
(
"
scroll
"
,
handleScroll
);
const
handleScroll
=
()
=>
{
if
(
document
.
body
.
scrollHeight
-
300
<
window
.
scrollY
+
window
.
innerHeight
)
{
setLoading
(
true
);
scrollHeight : It is the property that returns the total height of the content, including the portion that is not visible on the screen. So, it will be the total scrollable area.
scrollY: It is the property that returns the number of pixels that the document has been scrolled vertically from the top. So it will be the area that has been scrolled.
innerHeight: It is the property that return the height of the browser’s Windows content area. It will be the scrollbar width. It is added to scrollY so that fetch happens when we reached the content rather than when we passed the content.
## useEffect
After successfully changing state of
loading
, we can implement a useEffect to incrementing the page number. So that, the fetching of the movie data can happen.
useEffect
(()
=>
{
if
(
loading
==
true
)
{
setPage
((
prevPage
)
=>
prevPage
+
1
);
},
[
loading
]);
// other useEffect that we already implemented
useEffect
(()
=>
{
fetchMovie
();
},
[
page
]);
import
"
./App.css
"
;
import
{
useState
,
useEffect
}
from
"
react
"
;
import
axios
from
"
axios
"
;
import
{
MovieCard
}
from
"
./MovieCard
"
;
function
App
()
{
const
[
page
,
setPage
]
=
useState
(
1
);
const
[
data
,
setData
]
=
useState
([]);
const
[
loading
,
setLoading
]
=
useState
(
false
);
const
fetchMovie
=
async
()
=>
{
const
URL
=
`https://api.themoviedb.org/3/movie/popular?language=en-US&page=
${
page
}
`
;
const
data
=
await
axios
.
get
(
URL
,
{
headers
:
{
Authorization
:
"
Bearer API KEY
"
,
Accept
:
"
application/json
"
,
setData
((
prevData
)
=>
[...
prevData
,
...
data
.
data
.
results
]);
setLoading
(
false
);
useEffect
(()
=>
{
fetchMovie
();
},
[
page
]);
const
handleScroll
=
()
=>
{
document
.
body
.
scrollHeight
-
300
<
window
.
scrollY
+
window
.
innerHeight
setLoading
(
true
);
function
debounce
(
func
,
delay
)
{
let
timeoutId
;
return
function
(...
args
)
{
if
(
timeoutId
)
{
clearTimeout
(
timeoutId
);
timeoutId
=
setTimeout
(()
=>
{
func
(...
args
);
},
delay
);
window
.
addEventListener
(
"
scroll
"
,
debounce
(
handleScroll
,
500
));
useEffect
(()
=>
{
if
(
loading
==
true
)
{
setPage
((
prevPage
)
=>
prevPage
+
1
);
},
[
loading
]);
return
(
<
div
className
=
"App"
>
<
header
className
=
"App-header"
>
Popular movies according to Tmdb
<
div
className
=
"movieCardContainer"
>
{
data
.
length
>
1
&&
data
.
map
((
item
)
=>
{
return
(
<
MovieCard
key
=
{
item
.
id
}
title
=
{
item
.
original_title
}
description
=
{
item
.
overview
}
rating
=
{
item
.
vote_average
}
imageURL
=
{
item
.
poster_path
}
{
loading
&&
<
h1
>
Loading....
</
h1
>
}
</
div
>
</
header
>
</
div
>
export
default
App
;
Let's connect and stay informed on all things tech, innovation, and beyond!
Twitter
LinkedIn
Also, I am open to writing freelance articles if you are interested then contact me over email or social.
Building an infinite scroll component in React can be a highly rewarding experience. It not only enhances your understanding of how scrolling works but also teaches you about state management, event listeners, and optimization techniques like debouncing. By following this guide, you now have a basic infinite scroll setup that you can customize and improve according to your needs.
Whether you're displaying movie data, blog posts, or any other content, this component serves as a strong foundation. Remember, the key is to ensure a smooth user experience by carefully managing when and how data is fetched as the user scrolls. Happy coding!
Built on
Forem
— the
open source
software that powers
DEV
and other inclusive communities.
Made with love and
Ruby on Rails
. DEV Community
©
2016 - 2024.