react-infinite-scroller: loadMore function get called infinitely
I use React-Redux to pass loadMore and hasMore
Code:
import * as React from 'react';
import * as InfiniteScroll from 'react-infinite-scroller';
import styled from 'styled-components';
import { Item, SearchInput } from '../models/Search';
interface SearchResultProps {
input: SearchInput;
onLoad: (value: SearchInput) => void;
hasMore: boolean;
total: number;
items: Item[];
}
const Container = styled.div`
padding-top: 1rem;
display: flex;
flex-wrap: wrap;
justify-content: center;
`;
const ItemWrapper = styled.ul`
display: flex;
flex-wrap: wrap;
justify-content: center;
`;
const Item = styled.li`
margin: 1rem;
`;
const Poster = styled.img`
width: 10rem;
height: 14rem;
`;
const SearchResult = ({ items, total, onLoad, input, hasMore }: SearchResultProps) => {
const loader = (
<div key={0} style={{ clear: 'both' }}>
Loading ...
</div>
);
const loadMore = (page: number) => {
// Need to figure out why infinate loop
// tslint:disable-next-line:no-console
console.log(page);
onLoad({ ...input, page });
};
return (
<Container>
<InfiniteScroll pageStart={1} loadMore={loadMore} hasMore={hasMore} loader={loader}>
<div>{`${total} titles`}</div>
<ItemWrapper>
{items.map(item => (
<Item key={item.imdbID}>
<Poster src={item.Poster} alt={item.Title} />
</Item>
))}
</ItemWrapper>
</InfiniteScroll>
</Container>
);
};
export default SearchResult;
Type Black to the Title input and press Search
About this issue
- Original URL
- State: open
- Created 6 years ago
- Reactions: 23
- Comments: 33
The problem is because you are not passing or controlling the property
initialLoadcorrectly. This prop is true by default and every timecomponentDidUpdate()is executed, the methodscrollListener()is executed too.See code below:
I just passed the property
initialLoad={this.state.initialLoad}to InfiniteScroll component and addedthis.setState({ initialLoad: false });to loadMore function.Hope can help you.
So am I.
I use fetch request in
loadMore. addawaitand change thehasMoreoption. solved.Hope can help u.
After investigation, I think I know the reason now. See code below: https://github.com/CassetteRocks/react-infinite-scroller/blob/master/src/InfiniteScroll.js#L44
By design
componentDidUpdatewill be called every time when component receives new properties. In my example, the properties are{items, total, onLoad, input. hasMore}In Redux, when an action is dispatched, the component will receives new properties and
componentDidUpdatewill be triggered where newEventListenerforresizeandscrollwill be registered. However the old listeners still exist, so the infinite loop will happen.There are 2 solutions for this issue:
shouldComponentUpdateto avoid unnecessary update by returningfalseReduxand directly make server call within componentI have fixed my project by using solution 2:
https://github.com/changLiuUNSW/React-Open-Movie-Database/blob/master/src/components/SearchResult.tsx
Anyway, I think the author of this library should put some clarification on the documentation or example for the
ReduxIf the loadMore function call a lot, use ‘useWindow=false’.
I do that and it worked
Does anyone still have this issue while using Redux to supply hasMore
I solved this by adding a height to the div above the Infinite scroller.
No major changes, just added a parent div with a height. I don’t know what caused it but looks like the scroll isn’t detecting the div for the Infinite Scroll.
Hi, I used another solution, but I think this should be fixed inside the library. The solutions exposed in #204 seemed well suited to me, the guy who wrote the issue is up for the job of the PR, and I am too, but I don’t see much interaction from the author, so it may never get merged.
This was my solution, use the
hasMoreprop as a loading prop.So, basically set
hasMoretofalsewhen you start your ajax request, and when you receive the answer decide wether to change it to true again or leave like that.I experienced the same infinite reloads in Firefox (63.0.1) but not Chrome/Chromium(Opera). The reason however turned out to be different.
I had
<InifiteScroll />inside a<div style={{ overflow: auto }}>, which was itself inside a container<div style={{ display: 'flex', flexDirection: 'column' }}>. Turned out that Firefox (as opposed to Chrome) didn’t shrink the height of the flex container. As a result,parentNode.clientHeightwas always equal toel.scrollHeight, which caused the infinite scroll. https://github.com/CassetteRocks/react-infinite-scroller/blob/master/src/InfiniteScroll.js#L166I had to set
min-height: 0;on all flex parents of the container, which finally forced firefox to shrink the container properly. See this for details: https://stackoverflow.com/questions/28636832/firefox-overflow-y-not-working-with-nested-flexboxI am also facing the same issue
Yes @jieliu218 , we know that this happens. What have you tried as a remedy?
In my case, I immediately set the
hasMoreto false so loader will stop paging.you can see my repo link a couple of answers above.
You just saved my day, man. It was driving me insane that I couldn’t figure out why loadMore was getting triggered for apparently no reason. Thanks!
I have this same problem, loadMore get’s called infinitely. Though my situation is weirder. On Desktop view (at least 481px) it works fine, but when I reduce the size to mobile view (at most 480px), it gets called infinitely. Still looking into it, but if anybody has an idea why, please let me know.
Edit: Solved it. The problem was that I made the height of the container a smaller and fixed size on mobile, and changing the height to fit the content fixed it. The infinite loadMore seems to be a content overflow issue.
@jieliu218 you can see what I’m doing here. https://github.com/maxmedina05/react-product-page/blob/master/src/components/review-list/ReviewList.js
I just tested it on Chrome.
I’m using Next.js & Axios for sending request to API, but facing the same issue. I found that for it constantly sending requests rather than waiting for the response to the previous request. Due to that
hasMoreflag is not updating, and by default, I set it true in useState(). So, I need some additional condition to send requests along with the hasMore flag. I created theisLoadingstate, which will set true when the API request started. And it worked for me. 😋 Posting the entire implementation might be helpful to someone.I’ve managed to make my component load 1 page at a time fairly smoothly by implementing a modification to @OmanFerrer’s suggestion:
Instead of setting an initialLoad state, I’m setting this.initialLoad = true on the constructor and once the component is mounted I immediately set it to false. This fixed the infinite calls issue.
Then I noticed that depending on scroll behaviour it was calling my loadMore function 1 or multiple times before the component updates. I’ve remedied this by leveraging the hasMore property within the loadMore function. I set this.hasMore = true on the constructor, set it to false in the loadMore function, and set it to true again on componentDidUpdate. I will need to add additional checks on the componentDidUpdate to check if I’m at the end of my data set but so far this seems to be working well.
UPDATE I’m not setting state for hasMore and initialLoad, I’m just setting parameters on
this.I am also experiencing the exact same problem. Even though you may in fact be able to stop the hasMore by using state, it still interferes with the content loading and flow of the website, due to the loadMore is being called at least four times by default on page load. Because I was not aware of this at first, I managed to do what I believe was 1200 database calls at once through the loadMore function, which crashed Google Chrome.
I hope for a sophisticated universal solution for this.
Another solution is to always unregister all the
EventListenerbeforeattachScrollListenerget called, but need some clarification from the author whether this is a good approach or not