I have tried to do an infinite scroll by using React-Infinite-Scroll-Component
Everything is working perfectly as I think, but the problem is whenever I change the Genres, or press F5 to reload the page, or somehow, the scrollbar is always at the very bottom, I did try to fix it with window.scrollTo(0,0)
but it's still not working.
Or somehow do I mess up the code because I tried to work around it very hard, but I don't think I execute it well enough.
The code is very long but here's a brief explanation of what I'm trying to do in it.
I received a slug which defined as a genre
with useParams
, for example action
or adventure
, then I set it as a state genreAnime
, then I check if it's the same genre, it'll do the infinite load with concat array
and increase the page up to 1 for the next load, else I'll set a new array
and initial the page back to 1 or else it'll keep merging with an old array of previous genre
. The totalPage
is set to 91, whenever it renders for the second time it'll automatically get the latest totalPage
and set it back, so it's safe to go.
The translateGenre
is just for translating the genre into text, so please don't bother it.
But then whenever I refresh (F5) or whenever I browse for a bit, then I change the genre, it'll get a duplicate of the same first array
(due to warning and I see it has 2 same items). Due to the scrollbar of mine always staying at the "end" or at the default
value of React-Infinite-Scroll-Component, which is 0.8, it means when users come below 80% of the total height, it'll trigger the next
function of Infinite-Scroll-Component
Here's my code:
function AnimeGenre({ instance }) {
const { genre } = useParams()
const CancelToken = axios.CancelToken
const source = CancelToken.source()
const [animeList, setAnimeList] = useState([])
const [genreAnime, setGenreAnime] = useState("")
const [page, setPage] = useState(1)
const [totalPage, setTotalPage] = useState(91)
const [translateGenreAnime, setTranslateGenreAnime] = useState("")
const getList = async () => {
await instance
.get(`/anime/${genre}?page=${page}`, {
cancelToken: source.token,
})
.then((response) => {
const newPage = page + 1
setPage(newPage)
const newList = response.data.data.map((anime) => ({
slug: anime.slug,
thumbnail: anime.thumbnail,
name: anime.name,
views: anime.views,
}))
setTotalPage(response.data.pagination.totalPage)
setAnimeList((prev) => {
return [...new Set([...prev, ...newList])]
})
})
.catch((thrown) => {
if (axios.isCancel(thrown)) return
})
}
useEffect(() => {
if (genre === genreAnime) {
getList()
translateGenre()
} else {
window.onbeforeunload = function () {
window.scrollTo(0, 0)
}
window.scrollTo({
top: 0,
})
setPage(1)
setAnimeList([])
setGenreAnime(genre)
}
return () => {
source.cancel()
}
}, [genreAnime, genre])
const translateGenre = () => {
for (let i = 0; i < GENRES.length; i++) {
if (genreAnime == GENRES[i].slug) {
setTranslateGenreAnime(GENRES[i].name)
}
}
}
return (
<>
<div>
<h1>ANIME {translateGenreAnime}</h1>
</div>
<div className="anime-list">
<InfiniteScroll
initialScrollY={0}
style={{ overflow: "none" }}
dataLength={animeList.length}
next={getList}
hasMore={page === totalPage ? false : true}
loader={
<Row xs={1} sm={2} md={3} lg={4}>
{[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11].map((skeleton) => (
<Col key={skeleton}>
<Card>
<Card.Body style={{ maxHeight: "100%" }}>
<Skeleton
variant="rectangular"
width="100%"
height="192px"
animation="wave"
sx={{ bgcolor: "grey.900" }}
/>
<Skeleton
variant="text"
animation="wave"
sx={{ bgcolor: "grey.900" }}
/>
</Card.Body>
</Card>
</Col>
))}
</Row>
}
endMessage={<h4>NO MORE THINGS TO LOAD</h4>}
>
<Row xs={1} sm={2} md={3} lg={4}>
{animeList.map((anime) => (
<Col key={anime.slug}>
<Card>
<div className="card-container">
<Card.Img
variant="top"
src={anime.thumbnail}
fluid="true"
/>
<div className="overlay-card">
<a className="icon">{<BsFillPlayFill size={40} />}</a>
</div>
</div>
<Card.Body>
<Card.Title>
<TextTruncate
line={2}
element="span"
truncateText="…"
text={anime?.name}
/>
</Card.Title>
</Card.Body>
</Card>
<div className="w-100"></div>
</Col>
))}
</Row>
</InfiniteScroll>
</div>
<>
}
It's work fine whenever the scrollbar
stay at the very top, because it won't surpass the threshold of 80% of the height.
Working one:
Fail when I browse a bit and change the GENRE
, due to the scrollbar is stay at bottom, then later on, it'll do the scrollTo(0,0). Which is not what I want, I want the opposite, it must always go to top first...
The second fail is when I press reload the page with F5 button. It'll somehow display very weird (the scrollbar).
UPDATE:
I tried to disable smooth-scrolling behavior
and set up the scroll to top into the header
component that has the navbar, that's seem more proper way to do it, it's get my scrollbar to the top as I want whenever I clicked something in the header or refresh using F5.
But still if at the first time render, I browse the page and scroll down too fast, I'll get duplicate error due to I meet the threshold and the previous function is still being executed, if I wait for the first render to fully load, then pretty much I am fine, is there any proper way to do infinite scroll, please show me some simple way, I feel like I'm making it too compicated.
I think I got it right, but not so sure, it's working fine, "for now".
This is what I do.
Instead of
const newPage = page + 1
setPage(newPage)
inside the getList
function, I split it out into another function called scrollThreshold
const scrollThreshold = () => {
const newPage = page + 1
setPage(newPage)
}
and I set
const PAGE_NUMBER = 1
outside of the component (or else I think I should use useRef
or just leave it be, just in case)
then at the useEffect
, I implement 1 more dependencies, it's page
state, whenever the I get new page
or page
state change, or genre
change, it'll run the useEffect
again.
then at the InfiniteScroll
component, I slightly change it from
<InfiniteScroll>
...
next={getList}
...
</InfiniteScroll>
into
<InfiniteScroll>
...
next={getList && scrollThreshold}
...
</InfiniteScroll>
So whenever I scroll down, it'll trigger both getList and scrollThreshold, by being separated, it won't clump together like my first code. And so far it's working, I don't know if this is good or not.