Yes
Yes
Environment:
OS: macOS Sierra 10.12.6
Node: 6.10.3
Yarn: 1.0.2
npm: 3.10.10
Watchman: 4.7.0
Xcode: Xcode 8.3.3 Build version 8E3004b
Android Studio: Not Found
Packages: (wanted => installed)
react: 16.0.0-alpha.12 => 16.0.0-alpha.12
react-native: 0.48.3 => 0.48.3
<FlatList
data={this.state.data}
keyExtractor={(item, index) => index}
renderItem={this._renderItem}
onEndReached={this.onEndReached}
onEndReachedThreshold={0.5} />
onEndReached should not be called until the user scrolls down on the vertical FlatList.
onEndReached is called as the FlatList is loaded without any interaction from the end user. There seems to be some kind of race condition, as it only happens 50% of the time I load the screen. It is definitely reproducible with a couple refreshes.
Preferred way to reproduce is to download my minimal repo from https://github.com/zachrnolan/react-native-flatlist-vertical and yarn install && react-native run-ios
I also created a snack here: https://snack.expo.io/HkkO0RGob. Although it seems to be a little buggy with displaying the console logs.
Since starting with React Native about a year and a half ago, I've always dealt with some inconsistencies or bugs with lists and infinite scroll / paging. I've outlined my findings with FlatList to hopefully allow other people to better understand the landscape. This could be too much detail for this particular bug report, but I think it's helpful.
Platform | Number of Items | onEndReachedThreshold
--- | --- | ---
iOS | 5 (roughly half the screen height) | 0
Results:
onEndReached is called twice on load of the FlatList
Notes:
I'm guessing that onEndReached is called after the load of FlatList since the bottom of the FlatList is in view. It seems like the user should have to interact with the FlatList before onEndReached is called. Either way, shouldn't be called twice.
Platform | Number of Items | onEndReachedThreshold
--- | --- | ---
iOS | 10 (roughly the same height as the screen) | 0
Results:
onEndReached is called once on load of the FlatList
Notes:
Platform | Number of Items | onEndReachedThreshold
--- | --- | ---
iOS | 10 (roughly the same height as the screen) | 0.5 OR 1
Results:
Same as above
Platform | Number of Items | onEndReachedThreshold
--- | --- | ---
iOS | 10 (roughly the same height as the screen) | > 1
Results:
onEndReached is called multiple times on load of FlatList (typically around 10 times)
Notes:
Platform | Number of Items | onEndReachedThreshold
--- | --- | ---
iOS | 20 (roughly double the height of the screen) | 0
Results:
Notes:
This would be my preferred settings, however, the app I have in production is for both iOS and Android. These settings have another issue on Android (see below). I could check for Platform, so that may work.
Platform | Number of Items | onEndReachedThreshold
--- | --- | ---
iOS | 20 (roughly double the height of the screen) | 0.5
Results:
Platform | Number of Items | onEndReachedThreshold
--- | --- | ---
Android | 5 (roughly half the screen height) | 0
Results:
Platform | Number of Items | onEndReachedThreshold
--- | --- | ---
Android | 10 (roughly the same height as the screen) | 0
Results:
Android works as expected the first time you load more. However, it will not load a second time after you scroll to the bottom with the new data loaded. onEndReached function is not called.
Platform | Number of Items | onEndReachedThreshold
--- | --- | ---
Android | 10 (roughly the same height as the screen) | 0.5 OR 1
Results:
Platform | Number of Items | onEndReachedThreshold
--- | --- | ---
Android | 10 (roughly the same height as the screen) | > 1
Results:
onEndReached is called multiple times on load of FlatList (typically around 5 times)
Notes:
Platform | Number of Items | onEndReachedThreshold
--- | --- | ---
Android | 20 (roughly double the height of the screen) | 0
Results:
Platform | Number of Items | onEndReachedThreshold
--- | --- | ---
Android | 20 (roughly double the height of the screen) | 0.5
Results:
Seeing the same behavior on iOS / React Native 0.48.4
This is also an issue with horizontal FlatLists.
How make not fire onEndReached on load on iOS?
My application entering in infinite loop because of this.
hi (sorry for my English)
my solution:
I found this same issue - try putting onEndReached
function call in an anonymous function.
<FlatList
data={this.state.data}
keyExtractor={(item, index) => index}
renderItem={this._renderItem}
onEndReached={() => this.onEndReached()}
onEndReachedThreshold={0.5} />
Having the same issue. Im using a boolean flag to circumvent loading the data twice but it's obviously not a very elegant solution.
Another possible workaround https://github.com/facebook/react-native/issues/14015#issuecomment-310675650
I have not found a workaround that works...
but so far, if I remove this line, it stops doing the initial onEndReached
, but still fires when I actually reach the end.
Since I'm not a contributor, I'm not sure what the implications of removing this is.
Has anyone found a better solution? It's very frustrating to work with FlatList like this.
+1
`constructor(props){
super(props);
this.state = {
flatListReady:false
}
}
_scrolled(){
this.setState({flatListReady:true})
}
loadMore = () => {
if(!this.state.flatListReady){ return null}
//you can now load more data here from your backend
}
style={{width:'100%',flexGrow:1}}
ListHeaderComponent={this.headerComponent}
data={this.props.data}
renderItem={this.renderItem}
keyExtractor={(item,index) => index }
onEndReached={(x) => {this.loadMore()}}
onEndReachedThreshold={0.5}
/>
`
same problem here
react: ^16.0.0
react-native: 0.50.3
handleLoadMore() {
if(!this.props.refreshing){
console.log('handleLoadMore')
this.props.fetchProducts('down')
}
}
<FlatList
numColumns={2}
data={this.props.products}
renderItem={({item}) => <Product product={item}/>}
keyExtractor={(item, index) => index}
ListFooterComponent={this.renderFooter.bind(this)}
onRefresh={this.handleRefresh.bind(this)}
refreshing={this.props.refreshing}
onEndReached={this.handleLoadMore.bind(this)}
onEndReachedThreshold={0.5}
/>
+1
+1
Guys any update on the same? I am still facing the issue
I've tried all workarounds and none of them work 100% for me. This is the closest https://github.com/facebook/react-native/issues/14015#issuecomment-310675650 but still has some issue. It doesn't trigger when you scroll to end a second time.
Debouncing onEndReached
like this using lodash's debounce
or another debounce function works quite well.
constructor(props) {
super(props);
this._onEndReached = _.debounce(this._onEndReached, 500);
}
It stops multiple triggers onLoad (when list is shorter than screen height) and onEndReached.
Thanks for posting this! It looks like you may not be using the latest version of React Native, v0.53.0, released on January 2018. Can you make sure this issue can still be reproduced in the latest version?
I am going to close this, but please feel free to open a new issue if you are able to confirm that this is still a problem in v0.53.0 or newer.
This is reproduced in the latest version 0.53.0.
And it can't be pulled refresh if data is empty.
I just reopened this issue, because this bug still occurs in latest version of react-native.
@timwangdev so did you get any solution for the same?
For those of you that use native-base
, enclosing FlatList between <Content>
causes this issue.
Replacing the <Content>
with <View>
solved the issue in my case.
I'm wonder if there is any reason to call onEndReached if data is empty (data = [])?
_maybeCallOnEndReached() {
const {
data,
getItemCount,
onEndReached,
onEndReachedThreshold,
} = this.props;
const {contentLength, visibleLength, offset} = this._scrollMetrics;
const distanceFromEnd = contentLength - visibleLength - offset;
if (
onEndReached &&
this.state.last === getItemCount(data) - 1 &&
/* $FlowFixMe(>=0.63.0 site=react_native_fb) This comment suppresses an
* error found when Flow v0.63 was deployed. To see the error delete this
* comment and run Flow. */
distanceFromEnd < onEndReachedThreshold * visibleLength &&
(this._hasDataChangedSinceEndReached ||
this._scrollMetrics.contentLength !== this._sentEndForContentLength)
) {
// Only call onEndReached once for a given dataset + content length.
this._hasDataChangedSinceEndReached = false;
this._sentEndForContentLength = this._scrollMetrics.contentLength;
onEndReached({distanceFromEnd});
}
}
When your Flatlist component is mounting all checks(most likely) are true if data is empty :
If there are no such cases when it is necessary to call onEndReached with empty data,
then adding the check for data.length in if statement above will solve the problem i guess.
Hi Guys
Also, I am getting this while running tsc
command. (I am using NativeBase)
error TS2322: Type '{ refreshing: boolean; data: DataGridRow[]; renderItem: ({ item, index }: { item: any; index: any...' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes<Component<FlatListProperties<any>, ComponentState>...'.
Type '{ refreshing: boolean; data: DataGridRow[]; renderItem: ({ item, index }: { item: any; index: any...' is not assignable to type 'Readonly<FlatListProperties<any>>'.
Is anyone has any solution for this. I have updated my typescript with the latest version but still, I am getting this. Event the vscode is also showing the same.
"react-native": "0.54.4"
and still finding this issue, quire annoying to be honest
Hi All,
My solution here is to check first whether the Flatlist was already scrolled or not by using onScrollEndDrag property of Flatlist.
onEndReached will still be called for the first time, but you can prevent it before data fetching since the list was not scrolled yet (in my example its on _handleLoadMore_ function).
Please see my example code below.
constructor(props) {
super(props);
this.state = {isListScrolled: false};
}
handleLoadMore = () => {
if (isListScrolled) {
(Load more list....)
}
}
keyExtractor={(item, index) => index.toString()}
onEndReached={this.handleLoadMore}>
My react native version "react-native": "^0.52.2"
@patrickleemsantos
I think you will need to use something like this or else I get errors.
onScrollEndDrag={() => {this.setState({ isListScrolled: true }); }}
Also seeing this on "react-native": "0.54.4". Super duper annoying.
+1
Thanks for posting this! It looks like your issue may refer to an older version of React Native. Can you reproduce the issue on the latest release, v0.55?
Thank you for your contributions.
I can confirm that it still happens on 0.55 (specifically 0.55.2)
I am getting the error on 0.55 also
it's still happen
my environment
Packages: (wanted => installed)
react: ^16.3.1 => 16.4.1
react-native: ^0.55.4 => 0.55.4
+1
+1
it's still happen in 0.55.4.
I think setting Datas leads to FlatView rendering and trigger the event "onEndReached", I solved it by doing this:
componentDidMount() {
this.isScrolled = false;
}
getData = () => {
// this.setState({data: [.....]});
setTimeout(() => {
this.isScrolled = true;
}, 100);
}
onEndReached = () => {
if (!this.isScrolled) {
return null;
}
}
And that's why react in itself is really a problem for professional applications that don't have 500 devs to workaround stuff like airbnb had to (which are now moving away from RN again, like many others will). We won't see any fix from facebook if they don't have the same issue somewhere. They simply don't care.
Same issue in newest versions without any ui library. It also happens when bounce is deactivated.
It actually goes into an infinite loop until everything has been loaded. At no point the list has been touched. That's very likely because the flatlist doesn't read it's height correctly, but I'm just using View -> FlatList with nothing else at all.
System: OS: macOS 10.14 CPU: x64 Intel(R) Core(TM) i9-8950HK CPU @ 2.90GHz Memory: 13.74 GB / 32.00 GB Shell: 3.2.57 - /bin/bash Binaries: Node: 8.11.3 - /usr/local/bin/node Yarn: 1.10.1 - /usr/local/bin/yarn npm: 5.6.0 - /usr/local/bin/npm Watchman: 4.9.0 - /usr/local/bin/watchman SDKs: iOS SDK: Platforms: iOS 12.0, macOS 10.14, tvOS 12.0, watchOS 5.0 IDEs: Android Studio: 3.1 AI-173.4907809 Xcode: 10.0/10A255 - /usr/bin/xcodebuild npmPackages: react: 16.5.0 => 16.5.0 react-native: 0.57.2 => 0.57.2 npmGlobalPackages: react-native-cli: 2.0.1 react-native-git-upgrade: 0.2.7
For those of you that use
native-base
, enclosing FlatList between<Content>
causes this issue.Replacing the
<Content>
with<View>
solved the issue in my case.
There could also be issues when FlatList is inside another ScrollView.
React Native 0.57.2 and issue persists when the FlatList is inside a <ScrollView>
. Removing the ScrollView solves the problem.
@coocon It works.
Latest RN 0.57.4
Any updates? Issues exist more that 1 year, it easily reproduced on latest RN by placing FlatList
inside of ScrollView
and still not fixed
Even I didn't put the FlatList inside ScrollView, the problem still persists.
RN 0.57.0
For those of you that use
native-base
, enclosing FlatList between<Content>
causes this issue.
Replacing the<Content>
with<View>
solved the issue in my case.There could also be issues when FlatList is inside another ScrollView.
You saved my life man. 👍 💯
is there anybody care this problem.
RN 0.59.2 and this is still a problem.
This is a very mind-blowing thing for them to fix. I am sure we gonna have to wait another 2-3 years for it.
Pretty annoying.
Still has onEndReached bug, can not works well as the document said.
`constructor(props){
super(props);
this.state = {
flatListReady:false
}
}_scrolled(){
this.setState({flatListReady:true})
}loadMore = () => {
if(!this.state.flatListReady){ return null}//you can now load more data here from your backend
}
onScroll={this._scrolled.bind(this)}
style={{width:'100%',flexGrow:1}}
ListHeaderComponent={this.headerComponent}
data={this.props.data}
renderItem={this.renderItem}
keyExtractor={(item,index) => index }
onEndReached={(x) => {this.loadMore()}}
onEndReachedThreshold={0.5}/>
`
this works for me
the bug is still exist anyway, using Expo 34 --> RN 0.59.8
Edit: using FlatList from gesture-handler seems better
https://github.com/ifsnow/react-native-infinite-flatlist-patch
I made a patch library to help with this issue. I hope this helps.
the problem still exists in latest version of RN
Sorry for the bug and the radio silence here.
I finally dug into this and I believe the issue is indeed a race between onLayout
and onContentSizeChange
, which in general should be fine because there is no expectation of ordering between the two, and only causes issues with certain configurations.
The bug can be triggered if initialNumToRender
is smaller than needed to fill past the onEndReachedThreshold
(say the default, 10, is only 580px tall, but it takes 15 to reach the threshold). This will cause an incrementally render of more items to try and fill the viewport. The problem is that if the onLayout
comes back before the first onContentSizeChange
, it will first do the state increment to render 20 items and then the stale onContentSizeChange
callback from 10 items will fire and we'll think that the content size for 20 items is 580px when in fact it's 1160px (which is past the threshold). If those 20 items are also all of our available data, then we'll call onEndReached
because we think we've rendered everything and are still within the onEndReachedThreshold
.
The fundamental problem here is the system getting confused when a stale async onContentSizeChange
comes in after increasing state.last
. I wish there was a concrete timeframe, but Fabric will give us more flexibility to do things synchronously so hopefully we can avoid class of issues once that roles out.
There are a few workarounds you can try while you wait for a proper fix to be released:
1) Provide the getItemLayout
prop so the list doesn't have to rely on async layout data (you should do this whenever possible anyway for better perf). e.g. for the original snack example, you can just add getItemLayout={(d, index) => ({length: 58, offset: 58 * index, index})}
since all the rows are height 58 and the issue will no longer repro. Note this is fragile and must be kept in sync with UI changes, a11y font scaling, etc - a more robust approach could be to render a single representative row offscreen and measure it with onLayout
then use that value.
2) If getItemLayout
is not feasible to compute for your UI, increase initialNumToRender
to cover the onEndReachedThreshold
.
3) And/or add your own logic to protect against extra calls to onEndReached
as others have suggested.
This should be fixed in https://github.com/facebook/react-native/commit/8ddf231306e3bd85be718940d04f11d23b570a62, but it's still possible there are some related issues. Our plan is still to wait for fabric before doing a major overhaul / re-write of lists.
Hi guys, I am having the same problem too, but I have noticed this happening. On initial load when the array of data is of length 0
(no data), the onEndReached
doesn't trigger. But on subsequent navigation to this screen and the array is already populated with some data, I see that it triggers onEndReached
(with no scroll, on load)
The array is kept cuz I didn't want clear the array in reducer upon FETCH_DATA
action, cuz if there is some data previously fetched already, should be shown instead of the loader.
For those of you that use native-base, enclosing FlatList between
causes this issue.
Replacing the
with solved the issue in my case
This saves my day! Thanks!
It's not fixed yet and it's a horrible bug, I tried the solutions like flag and stuff but on different items for flatlist I got different results so the whole thing is useless
Still having this bug.
I literally deleted this feature from my app and added some item add the footer saying load more items...clicking that would load more items.Well it's terrible but at least I know it always works. Unlike the onEndReached which does wathever it wants whenever it wants and doesn't do anything it doesn't want to do.
Still having the bug, it has been literally 3 years!!
Most helpful comment
`constructor(props){
super(props);
this.state = {
flatListReady:false
}
}
_scrolled(){
this.setState({flatListReady:true})
}
loadMore = () => {
if(!this.state.flatListReady){ return null}
//you can now load more data here from your backend
}
onScroll={this._scrolled.bind(this)}
style={{width:'100%',flexGrow:1}}
ListHeaderComponent={this.headerComponent}
data={this.props.data}
renderItem={this.renderItem}
keyExtractor={(item,index) => index }
onEndReached={(x) => {this.loadMore()}}
onEndReachedThreshold={0.5}
`