In my React project I'm using react-infinite-scroller to fetch data from an API when a user scrolls to the bottom of a page. The first fetch call works as expected when the page loads, but as soon as I scroll down 50+ API calls are made and I get the Maximum update depth exceeded
error. Any ideas as to what I may be doing wrong?
response.data.next
is null
when page=2, but from what I can see hasMore
is always true
.
{"count":6,"next":null,...}
Tried using useRef
instead of useState
for hasMore
, but results are the same.
I'm following a basic example that looks like this:
import React, { useState, useEffect } from 'react';
import InfiniteScroll from 'react-infinite-scroller';
import Box from '@mui/material/Box';
import axiosInstance from '../axios';
const Test = () => {
const [data, setData] = useState([]);
const [hasMore, setHasMore] = useState(true);
const [page, setPage] = useState(1);
const fetchData = async (pageNumber) => {
try {
const response = await axiosInstance.get("/search/?page=" + (page))
const newData = response.data.results;
setData(prevData => [...prevData, ...newData]);
setHasMore(response.data.next !== null);
} catch (error) {
console.error('Error fetching data:', error);
}
};
useEffect(() => {
fetchData(page);
}, [page]);
const loadMore = () => {
setPage(prevPage => prevPage + 1);
};
return (
<>
<Box sx={{height: '800px'}}>
</Box>
<InfiniteScroll
pageStart={0}
loadMore={loadMore}
hasMore={hasMore}
loader={<div key={0}>Loading...</div>}
>
{data.map(item => (
<div key={item.id}>
{/* Render your data here */}
</div>
))}
</InfiniteScroll>
</>
);
};
export default Test;
The problem is in the way you are handling the page number. Infinity Scroll starts by calling the loadMore
, as you are changing the state by setting the page number, react triggers a rerender, when it gets rerendered, if Infinity Scroll has no elements, it calls the loadMore
again, as Infinity Scroll will get elements only after the API call, it will keep calling loadMore
in every rerender, and as the loadMore
trigger a rerender it causes an infinite rerender loop.
There are probably more ways to solve it, but handling the page number with a Ref instead of State fixed it:
import React, { useState } from 'react';
import InfiniteScroll from 'react-infinite-scroller';
import Box from '@mui/material/Box';
import axiosInstance from '../axios';
const Test = () => {
const [data, setData] = useState([]);
const [hasMore, setHasMore] = useState(true);
const page = useRef(1);
const fetchData = async (pageNumber) => {
try {
const response = await axiosInstance.get("/search/?page=" + pageNumber);
const newData = response.data.results;
setData(prevData => [...prevData, ...newData]);
setHasMore(response.data.next !== null);
} catch (error) {
console.error('Error fetching data:', error);
}
};
const loadMore = () => {
fetchData(page.current++);
};
return (
<>
<Box sx={{height: '800px'}}>
</Box>
<InfiniteScroll
pageStart={0}
loadMore={loadMore}
hasMore={hasMore}
loader={<div key={0}>Loading...</div>}
>
{data.map(item => (
<div key={item.id}>
{/* Render your data here */}
</div>
))}
</InfiniteScroll>
</>
);
};
export default Test;