I am implementing Bi-Directional Infinite Scroller in react js using Intersection Observer API that makes API calls in both direction, to optimize the rendering of the list I am storing contents of previousPage, currentPage & nextPage so it has a window of 3 if the user scrolls in either of the direction eg: Initial on page load it will call the api with page 0 and stores the response in state as users scrolls down it will append the new contents from the api to the state once the user passes page > 2(index starts of 0) it will flush the page 0’s content from the start and append page 3 at the end of the state. The same goes when user scrolls up until and the it passes the window scale it should remove the content from the last page and it should append the new page content to the start.
I have implemented the scrolling down logic but having issue implementing the scroll up logic.
import React, { useState, useEffect, useRef } from "react";
import axios from "axios";
function InfiniteScroller() {
const [page, setPage] = useState(0);
const [data, setData] = useState([]);
const [init, setInit] = useState(0);
const [loading, setLoading] = useState(true);
const WINDOW_SCALE = 3;
const observerRef = useRef(null);
const fetchData = async (pageNumber) => {
setLoading(true);
const res = await axios.get(
`https://api.instantwebtools.net/v1/passenger?page=${pageNumber}&size=20`
);
// When the user scrolls down and if the current page goes out of windows scale flush the first page contents
if (page === init + WINDOW_SCALE) {
setData((prevData) => {
const newArr = [...prevData];
const tempArr = newArr.slice(init + 20);
return tempArr;
});
setInit((prevInit) => prevInit + 1);
}
// When the user scrolls up and if the current page goes out of windows scale flush the last page contents
if (res.data.data.length) {
res.data.data.map((dt) => {
setData((prevData) => [...prevData, dt]);
setLoading(false);
});
}
};
useEffect(() => {
fetchData(page);
}, [page]);
const handleIntersection = (entries) => {
console.log(page);
entries.forEach((entry) => {
if (entry.isIntersecting) {
if (entry.target.id === "bottom") {
// user has scrolled to bottom of page, fetch more data
setPage((prevPage) => prevPage + 1);
} else if (entry.target.id === "top") {
// user has scrolled to top of page, fetch more data
}
}
});
};
useEffect(() => {
observerRef.current = new IntersectionObserver(handleIntersection, {
threshold: "0.7",
});
observerRef.current.observe(document.getElementById("bottom"));
page > 0 && observerRef.current.observe(document.getElementById("top"));
return () => observerRef.current.disconnect();
}, []);
return (
<div
style={{
height: "300px",
overflow: "scroll",
border: "1px solid black",
margin: "5em",
overflowX: "hidden",
textAlign: "center",
}}
>
{page > 1 && <div id="top" />}
{data.map((passenger, i) => (
<div key={i}>
<p>Name: {passenger.name}</p>
<p>Trips: {passenger.trips}</p>
</div>
))}
<div id="bottom" />
{loading && <p>Loading</p>}
</div>
);
}
export default InfiniteScroller;
CodeSandbox: Sample Code