FlatList calls `onEndReached` when it's rendered

React Native

React Native Problem Overview


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

React Native Solutions


Solution 1 - React Native

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 - React Native

I've got it working with

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

Solution 3 - React Native

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 - React Native

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 - React Native

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 - React Native

Remove every Scrollable View inside your FlatList

Solution 7 - React Native

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 - React Native

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 - React Native

(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 - React Native

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 - React Native

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 - React Native

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 - React Native

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 - React Native

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 - React Native

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 - React Native

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.

Solution 17 - React Native

I have solved it with using debounce from lodash. Firstly, I import debounce from 'lodash.debounce'. Then I use debounce for load more function with 500 ms interval

<Flatlist onEndReached = {debounce(this._onLoadMore, 500)} />

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionmerry-go-roundView Question on Stackoverflow
Solution 1 - React NativeIlarioView Answer on Stackoverflow
Solution 2 - React NativeloucidityView Answer on Stackoverflow
Solution 3 - React NativeNagibabaView Answer on Stackoverflow
Solution 4 - React NativeLeandroGView Answer on Stackoverflow
Solution 5 - React NativeVictor MolinaView Answer on Stackoverflow
Solution 6 - React NativeHarsha KoshilaView Answer on Stackoverflow
Solution 7 - React NativeTim GarciaView Answer on Stackoverflow
Solution 8 - React NativeRafView Answer on Stackoverflow
Solution 9 - React NativeNishant KoliView Answer on Stackoverflow
Solution 10 - React NativeHossein MohammadiView Answer on Stackoverflow
Solution 11 - React NativeBilal AbdeenView Answer on Stackoverflow
Solution 12 - React NativeKasraView Answer on Stackoverflow
Solution 13 - React NativeBuffaloDevView Answer on Stackoverflow
Solution 14 - React NativeRanaBestView Answer on Stackoverflow
Solution 15 - React NativeamosgamalielView Answer on Stackoverflow
Solution 16 - React NativeFernando BritoView Answer on Stackoverflow
Solution 17 - React Nativequynhnguyen68View Answer on Stackoverflow