React-native: FlatList onEndReached triggered before reach onEndReachedThreshold

Created on 9 Mar 2017  路  40Comments  路  Source: facebook/react-native

Description

FlatList onEndReached triggered before reach end of list. If legacyImplementation is set to true or render with ListView, onEndReached will trigger correctly when it reached end of list

Reproduction

Based from FlatListExample on UIExplorer in React Native v0.43.0-rc.1, add onEndReached to FlatList

state = {
  data: genItemData(30),
  debug: false,
  horizontal: false,
  filterText: '',
  fixedHeight: true,
  logViewable: false,
  virtualized: true,
};

<FlatList
  HeaderComponent={HeaderComponent}
  FooterComponent={FooterComponent}
  SeparatorComponent={SeparatorComponent}
  data={filteredData}
  debug={this.state.debug}
  disableVirtualization={!this.state.virtualized}
  getItemLayout={this.state.fixedHeight ?
    this._getItemLayout :
    undefined
  }
  horizontal={this.state.horizontal}
  key={(this.state.horizontal ? 'h' : 'v') +
    (this.state.fixedHeight ? 'f' : 'd')
  }
  legacyImplementation={false}
  numColumns={1}
  onRefresh={this._onRefresh}
  onViewableItemsChanged={this._onViewableItemsChanged}
  ref={this._captureRef}
  refreshing={false}
  renderItem={this._renderItemComponent}
  shouldItemUpdate={this._shouldItemUpdate}
  viewabilityConfig={VIEWABILITY_CONFIG}
  onEndReachedThreshold={1}
  onEndReached={({ distanceFromEnd }) => {
    console.log('on end reached ', distanceFromEnd)
  }}
/>  

Additional Information

  • React Native version: v0.43.0-rc.1
  • Platform: Both
  • Operating System: MacOS
Locked

Most helpful comment

I believe Content is from NativeBase and I believe that implements a ScrollView. FlatList cannot work right inside a ScrollView.

All 40 comments

@alphasp I have not yet played around with FlatList, but is there any chance that the onEndReachedThreshold has some default non-zero value you are unaware of?

@ramilushev Yes, just checked the code, created a PR to make it behave like ListView implementation

@sahrens Can you have a look at this issue and my pull request?

What if you set the threshold to zero?

@sahrens If onEndReachedThreshold is set to zero, onEndReached will be trigger when reach end of list. However if the threshold is set to more than 0, for example 10, onEndReached will be trigger before it go past the threshold value due to this line
https://github.com/facebook/react-native/blob/master/Libraries/CustomComponents/Lists/VirtualizedList.js#L591
where you compare distanceFromEnd < onEndReachedThreshold * visibleLength
On my iPhone 7 Simulator, when running the FlatListExample, the values will be distanceFromEnd < 10 * 504.5, which will trigger onEndReached immediately before it go past the threshold value

I also encountered this problem, hope the next version can fix it.

I know how to solve this, you just need to set onEndReachedThreshold as a rate of visibleLength.
So you just need to set it as a number smaller than 1, then it worked!!!!!

If onEndReachedThreshold > 0 then it is triggered 2 times..

The denouncing is based on state changes, so if you rerender anything or update the data in response to the callback or after it's called the first time, it might get called again.

My error in Production:

com.facebook.react.common.JavascriptException: undefined is not a function (evaluating 't._ItemLoadMore()'), stack:
onEndReached@574:4466
_onScroll@277:16504
scrollResponderHandleScroll@263:1975
_handleScroll@261:2584

I think EndReached has problem.

@thiensubs It looks like you're providing a callback that may not be correctly bound so not sure what would be wrong with FlatList. Do you have more sample code that shows what you're doing and may reveal the bug?

@sahrens My Flatlist like this:

 var flatList = <FlatList
                style={styles.lStyle }
                data={this.state.dataSource2}
                renderItem={ItemRowYesterdaySignal.bind(this)}
                onEndReachedThreshold={10}
                onEndReached={({ distanceFromEnd }) => {
                   this._ItemLoadMore();
                }}
                ref='_flatList'
            />;
_ItemLoadMore(){
        if (this.state.current_page +1 <= this.state.total_pages)
        {
            if (this.keyword  && this.keyword !='')
            {
                var page = this.state.current_page+1;
                this.search(this.keyword, page)
            }
            else
            {
                this.loadMore(this.state.current_page+1)
            }
        }
    }

Thanks for your comment.

Can you provide more context including the lifecycle of the component that renders the FlatList and where you use the ref?

Yes. It like pagination, when I scroll to onEndReachedThreshold 10, it will call LoadMore, it repeat again with condition
this.state.current_page +1 <= this.state.total_pages
I don't use ref any where, I just define that.
Do you need image for list?

also encountering this problem

same problem here

+1 !!!
I scroll a little bit and "onEndReached" called already. ListView did not have that problem!

I just replaced ListView with FlatList. All props and code the same.

@sahrens

onEndReachedThreshold?: ?number

How far from the end (in units of visible length of the list) the bottom edge of the list must be from the end of the content to trigger the onEndReached callback. Thus a value of 0.5 will trigger onEndReached when the end of the content is within half the visible length of the list.

As quoted in updated doc regarding onEndReachedThreshold on http://facebook.github.io/react-native/releases/0.44/docs/flatlist.html#onendreachedthreshold
We will need to set value range from 0 to 1 where value of 0.5 will mean half the visible length of the list

So on every onEndReached event i'm increasing my page number by 1. Would this be considered normal in a common app? or should I stop increasing the number if there are no more results to fetch?

Also, I noticed the when scrolling back up, sometimes endReached gets called again...

@msqar no you have to pick a value between 0 and 1 and stick to it. It's about when FlatList will get the new data, i.e, how far into the current view before it goes and get new data. 0 is the start and 1 is the end. When you get to the end and your Threshold is 1 it will get more data and at that point you're back at 0. It would be odd if the designers of FlatList expected the user to increment the threshold.

@idibidiart Ok, i will set it somewhere between 0.5 and 0.8. But is it good to have the page increasing by one all the time unless you pull up to refresh which resets it to 1 again? Or should i implement it differently?

0.5 - 0.8 sounds good ... try to make any data processing loop asynchronous, e.g. with requestAnimationFrame (async re-entrant loop) so it does not hold up the list scrolling while you're transforming the data, that is if you have ay transforming to do.

What do you mean by page increasing by one all the time? The threshold should have nothing to do with the page number.

Maybe I'm missing something?

@idibidiart I'm doing infinite scrolling (pagination) and using FlatList. Page is just a state value I have to determine what page number is going to be sent to the REST API. And onEndReached event I just increase the page number by 1. That way it keeps sending the pages to my app. But I noticed that whenever i scrolled at max and got all the pages from DB, if i start to scroll up, the endReached event is called again with no reason... is that normal?

I assume that when using FlatList you need to keep the current materialized view's data and some buffer above it and below it, or else if you will run out of memory eventually. The event firing makes sense but the way tou handle it is up to you. FlatList is not fetching the data for you. It just tells you when the current Threshold has been reached, in either direction. Makes sense to me. I have to admit I just got to play with FlatList and have not used it in any real scenario yet.

Does this help?

@idibidiart yes :) then i think is fine then! Thanks!

For folks reporting the onEndReached callback being called twice on iOS, this may be caused by the ScrollView bouncing effect, as described in this thread: https://github.com/facebook/react-native/issues/14015

I also posted my solution there.

i tried setting onEndReachedThreshold to 0.5,, now I'm halfway of my flat list, and onEndReached() is not triggered, i tried scrolling past it, reaching the very end of the list, still the onEndReached() is not triggered.

Why is it not working on android?

<Content> <FlatList data={this.props.destination_place_list} renderItem={this.renderItem} keyExtractor={item => item.id} onEndReached={(xxx) => { console.log(xxx); this.handleLoadMore.bind(this); } } onEndReachedThreshold={0.5} /> </Content>

I'm using android and react-native 0.45. Have you guys fixed the issue?

I believe Content is from NativeBase and I believe that implements a ScrollView. FlatList cannot work right inside a ScrollView.

@idibidiart I have this problem in my code and how i can fix it ? tnx

I have this problem too it really needs to be fixed , it really drive me crazy ! (and yes i used it inside the Container component from NativeBase )

Is there any function called when scroll reached start. Something like onStartReached() ???

@vigor87 What are you trying to achieve? I'm using the onScroll prop on FlatList and saving it's scroll-y value into a React.Component's state to decide whether to show an image shadow or not. When the scroll-y value is 0 the shadow is displayed with no opacity at all.

I'm noticing that when using onEndReached to load more content and there is no more content, but the data array is changed (because loading nothing lead to a new identical array being created) and therefore a rerender is triggered with the cursor position is at the end, onEndReached is called again and an infinite loop occurs. I would suggest that onEndReached only gets triggered during or after scrolling.

Try this
onEndReachedThreshold={0.01}

@vigor87 did you find onStartReached() ?

@priyankinfinnov have you tried using onScroll?

I understand @sahrens 's comment "The denouncing is based on state changes, so if you rerender anything or update the data in response to the callback or after it's called the first time, it might get called again."

But how can we prevent it from calling onEndReached when it's re-rendered???

return (
  <View style={{ flex:1 }}>
    <FlatList
      data={this.props.categories}
      renderItem={this._renderItem}
      keyExtractor={this._keyExtractor}
      numColumns={2}
      style={{flex: 1, flexDirection: 'row'}}
      contentContainerStyle={{justifyContent: 'center'}}
      refreshControl={
        <RefreshControl
          refreshing = {this.state.refreshing}
          onRefresh = {()=>this._onRefresh()}
        />
      }
      // curent value for debug is 0.5
      onEndReachedThreshold={0.5} // Tried 0, 0.01, 0.1, 0.7, 50, 100, 700

      onEndReached = {({distanceFromEnd})=>{ // problem
        console.log(distanceFromEnd) // 607, 878 
        console.log('reached'); // once, and if I scroll about 14% of the screen, 
                             //it prints reached AGAIN. 
        this._onEndReachedThreshold()
      }}
    />
  </View>
)

More:
https://stackoverflow.com/questions/47910127/flatlist-calls-enendreached-when-its-rendered

Hi,

Please check if you are using minimum value in an array because I had faced this issue and correct.

`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}

       />

`

Ok so in my case issue was because I was using Listview inside Content from NativeBase using it inside Container fixed the issue

Was this page helpful?
0 / 5 - 0 ratings