'FlatList calls `onEndReached` when it's rendered

Here is render() function for my simple category list page.

Recently I added pagination for my FlatList View so when the user scrolls to the bottom, onEndReached is called in a certain point(onEndReachedThreshold value length from the bottom), and it will fetch the next categories and concatenate the categories props.

But my problem is onEndReached is called when render() is called In other words, FlatList's onEndReached is triggered before it reach the bottom.

Am I putting wrong value for onEndReachedThreshold? Do you see any problem?

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>
)

UPDATE I fetch this.props.categories data here

  componentWillMount() {
    if(this.props.token) {
      this.props.loadCategoryAll(this.props.token);
    }
  }


Solution 1:[1]

Try to implement onMomentumScrollBegin on FlatList :

constructor(props) {
    super(props);
    this.onEndReachedCalledDuringMomentum = true;
}

...

<FlatList
    ...
    onEndReached={this.onEndReached.bind(this)}
    onEndReachedThreshold={0.5}
    onMomentumScrollBegin={() => { this.onEndReachedCalledDuringMomentum = false; }}
/>

and modify your onEndReached

onEndReached = ({ distanceFromEnd }) => {
    if(!this.onEndReachedCalledDuringMomentum){
        this.fetchData();
        this.onEndReachedCalledDuringMomentum = true;
    }
}

Solution 2:[2]

I've got it working with

<Flatlist
    ...
    onEndReached={({ distanceFromEnd }) => {
        if (distanceFromEnd < 0) return;
        ...
    }
    ...
/>

Solution 3:[3]

First check if the FlatList is inside a ScrollView or Content of native-base. Then take it outside of it Actually you don't need to use Content or ScrollView, as FlatList has both ListFooterComponent and ListHeaderComponent.

Though it is not recommended, if you really need to use Flatlist inside ScrollView, then take a look at this answer: https://stackoverflow.com/a/57603742/6170191

Solution 4:[4]

After hours of trying different approaches I got it to work by wrapping the Flatlist with a View of fixed height and flex:1. With this settings, I was able to get onEndReached called once and only after I scroll near the bottom. Here's my code sample:

render() {
    const {height} = Dimensions.get('window');
    return (    
        <View style={{flex:1, height:height}}>
            <FlatList
                data={this.props.trips_uniques}
                refreshing={this.props.tripsLoading}
                onRefresh={()=> this.props.getTripsWatcher()}
                onEndReached={()=>this.props.getMoreTripsWatcher()}
                onEndReachedThreshold={0.5}
                renderItem={({item}) => (
                <View style={Style.card}> 
                    ...
                    ...
                </View>
                )}
                keyExtractor={item => item.trip_id}
            />
        </View>
    )
}

My onEndReached() function just calls the API and updates my data. It doesn't do any calculations with regards to distance to bottom or threshold

Solution 5:[5]

Most of the times, this error is caused because of an incorrect use of onEndReachedThreashold, which also depends of the number of items you are rendering (more items, more scroll size).

Try to follow this logic:

If 10 items cover your screen, and you are rendering 20 items on each scroll, then set onEndReachedThreashold to 0.8.

If 2 or 3 items cover your screen, and you are rendering 10 items on each scroll, then set onEndReachedThreashold to 0.5.

Also, use initialNumToRender = numItems. For some reason, using this FlatList prop helps to reduce the chance of multiple onEndReached calls.

Just play with onEndReachedThreashold value.


Other times, this error is produced because of nesting scroll views. Do not put your FlatList inside of a ScrollView. Instead, take use of the FlatList header and footer props.


For both solutions, I suggest to set the FlatList style and contentContainerStyle to { flexGrow: 1 }.

Solution 6:[6]

Remove every Scrollable View inside your FlatList

Solution 7:[7]

If you want to show 3 or 4 records and want to load the next data just when you reach the end. Set onEndReachedThreshold to 0 or 0.1.

Solution 8:[8]

Maybe You can bypass this FlatList bug by incrementing your page before doing async call, and then you will fetch data on every onEndReached fiers and not get errors about duplicate keys

Solution 9:[9]

(as of NOV19)

  1. Keep flatlist as the only component inside of a single view
  2. Set style of that single view from dimensions like
    {{flex: 1, height: Dimensions.get('window').height}}

Solution 10:[10]

If FlatList is on another FlatList or ScrollView the onEndReached call immediately when rendered component to resolve that problem doesn't wrap FlatList with another.

Solution 11:[11]

I have a <FlatList> (from react-native) inside an <Overlay> (from react-native-elements.) I have the problem of onEndReached being executed as soon as the component is rendered for the 1st time and before the user does anything.

The problem was resolved by using <Modal> (from react-native), instead of <Overlay>.

Solution 12:[12]

If you are using hooks, here you can find the hook version of @Ilario answer:

const onEndReachedCalledDuringMomentum = useRef(true)

onEndReachedHandler = ({ distanceFromEnd }) => {
    if(!onEndReachedCalledDuringMomentum.current){
        fetchData()
        onEndReachedCalledDuringMomentum.current = true
    }
}

<FlatList
    ...
    onEndReached={onEndReachedHandler}
    onEndReachedThreshold={0.7}
    onMomentumScrollBegin={() => { onEndReachedCalledDuringMomentum.current = false }}
/>

Solution 13:[13]

This simple solution worked for me. Note the "refreshing" state is controlled by an async API call in a useEffect hook to retrieve data for the FlatList.

const onEndReachedHandler = () => {
    if (!refreshing) ...
}

<FlatList
    ...
    data={mydata}
    onEndReached={onEndReachedHandler}
    onEndReachedThreshold={0.7}
    refreshing={refreshing}
/>

Solution 14:[14]

I struggled around the whole day but the issue that I was getting is, I am using FlatList inside ScrollView. So, Remove Scrollview & then use Flatlist independently. This will solve my problem.

Solution 15:[15]

From my experience, you can simply utilize onEndReachedThreshold props in your FlatList or SectionList and pass a very very small number like 0.001 to it.

onEndReachedThreshold={0.001}

According to docs for FlatList, onEndReachedThreshold is units of length from the bottom in list items.

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. For example, a value of 0.5 will trigger onEndReached when the end of the content is within half the visible length of the list.

Thus, a very small value like 0.001 helps you to make sure that onEndReached is only gonna be called when the end of the content is within the very end of the visible length of the list.

Hope this helps :) Sorry for bad English.

Solution 16:[16]

A bit late but I just ran into this issue and I fixed it by passing to my <FlatList/> the initialNumToRender prop. This prop is 10 by default so if you don't set it and your screen shows more than 10 items on the initial render, it is going to trigger onEndReached since it has passed the 10th element.

initialNumToRender should probably be the same as the amount of elements you fetch per page.