Reputation: 845
I'm using React Infinite Scroller to render a list of posts.
My load more results function is called 3 times and therefore the result is updated three times (can be seen with a console.log)
I render the posts with:
{results.map((result) => (
<Widget data={result} key={result.id} />
))}
This updates the result the first two times, but when the state changes for the third time, it doesn't render any more results.
My full code:
import React, { useState } from "react";
import InfiniteScroll from "react-infinite-scroller";
import LoadingIndicator from "./loading";
import { request } from "../communication";
const InfiniteResults = ({ endpoint, widget }) => {
let [results, setResults] = useState([]);
let [hasMoreResults, setHasMoreResults] = useState(true);
const Widget = widget;
function loadMoreResults(page) {
// prevent from making more requests while waiting for the other one to complete
setHasMoreResults(false);
request
.get(endpoint, {
offset: page * 10,
})
.then(function gotResults(response) {
const data = response.data;
const resultsCopy = results;
data.results.forEach(function saveResultToState(result) {
resultsCopy.push(result);
});
setResults(resultsCopy);
console.log(resultsCopy);
console.log(results);
if (!data.next) {
setHasMoreResults(false);
} else {
setHasMoreResults(true);
}
});
}
return (
<InfiniteScroll
pageStart={-1}
loadMore={loadMoreResults}
hasMore={hasMoreResults}
loader={<p key={0}>Loading...</p>}
>
{results.map((result) => {
console.log("rerender");
return <Widget data={result} key={result.id} />;
})}
</InfiniteScroll>
);
};
export default InfiniteResults;
Upvotes: 0
Views: 71
Reputation: 2342
Your problem is with your state update:
const resultsCopy = results;
data.results.forEach(function saveResultToState(result) {
resultsCopy.push(result);
);
setResults(resultsCopy);
When you do const resultsCopy = results;
, you're not actually copying the array; you're just referring to the same array by another name. That means that when you start adding elements to it, you are adding them directly to the one controlled by React's useState
hook. All state updates should be done through the setState
(setResults
in your case) function that useState
returns.
To perform an update on the previous state, the best way to do that is to call the setResults
function with a function as an argument, that takes the previous state as its own argument. This ensures that the state updates are sequential (each update happens after the previous one was completed).
setResults(prevState => prevState.concat(data.results));
// or
setResults(function (prevState) {
return prevState.concat(data.results);
});
Here I have also made use of the concat
function that combines two arrays into one and returns the result. That way you are not updating the previous state with the push
calls.
Upvotes: 2