Detect ScrollView has reached the end
Asked Answered
H

11

96

I have a Text with long text inside a ScrollView and I want to detect when the user has scrolled to the end of the text so I can enable a button.

I've been debugging the event object from the onScroll event but there doesn't seem any value I can use.

Harrie answered 9/12, 2016 at 9:8 Comment(0)
A
201

I did it like this:

import React from 'react';
import {ScrollView, Text} from 'react-native';
    
const isCloseToBottom = ({layoutMeasurement, contentOffset, contentSize}) => {
  const paddingToBottom = 20;
  return layoutMeasurement.height + contentOffset.y >=
    contentSize.height - paddingToBottom;
};
    
const MyCoolScrollViewComponent = ({enableSomeButton}) => (
  <ScrollView
    onScroll={({nativeEvent}) => {
      if (isCloseToBottom(nativeEvent)) {
        enableSomeButton();
      }
    }}
    scrollEventThrottle={400}
  >
    <Text>Here is very long lorem ipsum or something...</Text>
  </ScrollView>
);
    
export default MyCoolScrollViewComponent;

I wanted to add paddingToBottom because usually it is not needed that ScrollView is scrolled to the bottom till last pixel. But if you want that set paddingToBottom to zero.

Acaulescent answered 9/12, 2016 at 10:34 Comment(6)
Note that if your content is shorter than the container, then this will trigger always. If you have a case where your paddingToBottom is negative (e.g. for handling overscroll), then make sure to handle this situation separately (basically by contentOffset.y > -paddingToBottom (note the resulting double negative))Decoct
This is not triggering "always" for me. onScroll only is called when the user scrolls.Jespersen
in scroll end it is working perfect but when I scroll up from last ending point then it called. So any solution to stop this issue when scroll up?Statehood
I had that same problem, on IOS it worked great but android was glitchy. Try using onMomentumScrollEnd instead which calls an event every time the scroll stops.Whitney
Note that the parameters are destructured twice in his example. It's prone to making mistake.Hereinto
I have implemented the above code and it worked for both iOS and Android, but it is not working on my web browser. Any ideas?Mediocrity
S
48

As people helped here I will add the simple code they write to make reached to top and reached to bottom event and I did a little illustration to make things simpler

Layout and vars values

<ScrollView
onScroll={({nativeEvent})=>{
if(isCloseToTop(nativeEvent)){
    //do something
}
if(isCloseToBottom(nativeEvent)){
   //do something
}
}}
>
...contents
</ScrollView>



isCloseToBottom({layoutMeasurement, contentOffset, contentSize}){
   return layoutMeasurement.height + contentOffset.y >= contentSize.height - 20;
}



ifCloseToTop({layoutMeasurement, contentOffset, contentSize}){
   return contentOffset.y == 0;
}
Sourdine answered 28/10, 2018 at 18:57 Comment(0)
V
19
<... onScroll={(e) => {
        let paddingToBottom = 10;
        paddingToBottom += e.nativeEvent.layoutMeasurement.height;
        if(e.nativeEvent.contentOffset.y >= e.nativeEvent.contentSize.height - paddingToBottom) {
          // make something...
        }
      }}>...

like this react-native 0.44

Vestryman answered 13/6, 2017 at 22:0 Comment(0)
I
7

For Horizontal ScrollView (e.g. Carousels) replace isCloseToBottom function with isCloseToRight

isCloseToRight = ({ layoutMeasurement, contentOffset, contentSize }) => {
    const paddingToRight = 20;
    return layoutMeasurement.width + contentOffset.x >= contentSize.width - paddingToRight;
};
Inerasable answered 23/8, 2017 at 9:59 Comment(1)
how about close to left? is this possible?Selfassurance
S
5

Another solution could be to use a ListView with a single row (your text) which has onEndReached method. See the documentation here

Saprolite answered 9/12, 2016 at 23:8 Comment(3)
But this would require that each paragraph of the text to be treated with a DataSource and create a component (a simple Text would do). Not a bad idea, but might be a little bit more complex to implement.Harrie
Your onEndReached has saved me a lot of time.Shaunna
ListView is deprecated. reactnative.dev/blog/2017/03/13/better-list-views.htmlMyrnamyrobalan
S
5

@Henrik R's right. But you should use Math.ceil() too.

function handleInfinityScroll(event) {
        let mHeight = event.nativeEvent.layoutMeasurement.height;
        let cSize = event.nativeEvent.contentSize.height;
        let Y = event.nativeEvent.contentOffset.y;

        if(Math.ceil(mHeight + Y) >= cSize) return true;
        return false;
}

enter image description here

Scotism answered 29/11, 2018 at 7:54 Comment(1)
why should you use the Ceil?Deathlike
O
5
const isCloseToBottom = ({ layoutMeasurement, contentOffset, contentSize }) => {
const paddingToBottom = 120
return layoutMeasurement.height + contentOffset.y >= contentSize.height - paddingToBottom}


 <ScrollView
    onMomentumScrollEnd={({ nativeEvent }) => {
      if (isCloseToBottom(nativeEvent)) {
        loadMoreData()
      }
    }}
    scrollEventThrottle={1}
  >

Above answer is correct but to callback on reaching the end in scrollView use onMomentumScrollEnd not onScroll

Osbourn answered 29/6, 2021 at 13:59 Comment(1)
you should remove that async keyword, it's useless, and because of that the isCloseToBottom result will always be detected as trueMummy
I
2

As an addition to the answer of Henrik R:

If you need to know wether the user has reached the end of the content at mount time (if the content may or may not be too long, depending on device size) - here is my solution:

<ScrollView 
        onLayout={this.onLayoutScrollView}
        onScroll={this.onScroll}>
    <View onLayout={this.onLayoutScrollContent}>
        {/*...*/}
    </View>
</ScrollView>

in combination with

onLayout(wrapper, { nativeEvent }) {
    if (wrapper) {
        this.setState({
            wrapperHeight: nativeEvent.layout.height,
        });
    } else {
        this.setState({
            contentHeight: nativeEvent.layout.height,
            isCloseToBottom:
              this.state.wrapperHeight - nativeEvent.layout.height >= 0,
        });
    }
}
Incarnadine answered 5/7, 2018 at 10:7 Comment(0)
O
1

I use ScrollView and this worked for me

Here is my solution:

I passed onMomentumScrollEnd prop to scrollView and on the basis event.nativeEvent I achieved onEndReached functionality in ScrollView

 onMomentumScrollEnd={(event) => { 
          if (isCloseToBottom(event.nativeEvent)) {
            LoadMoreRandomData()
          }
         }
       }}

  const isCloseToBottom = ({layoutMeasurement, contentOffset, contentSize}) => {
const paddingToBottom = 20;
return layoutMeasurement.height + contentOffset.y >=
  contentSize.height - paddingToBottom;

};

Onstage answered 13/3, 2021 at 20:32 Comment(0)
C
0

you can use this function onMomentumScrollEnd to know scroll information (event)

<ScrollView onMomentumScrollEnd={({ nativeEvent }) => {
                handleScroll(nativeEvent)
            }}>

and with these measure (layoutMeasurement.height + contentOffset.y >= contentSize.height - paddingToBottom)

you can know if the scroll is at the end

const handleScroll = ({ layoutMeasurement, contentOffset, contentSize }) => {
        const paddingToBottom = 20;
        if (layoutMeasurement.height + contentOffset.y >= contentSize.height - paddingToBottom) {
            ...
        }
    };
Caprine answered 22/7, 2022 at 17:32 Comment(0)
L
-1

disregard all convoluted answers above. this works.

import React, { useState } from 'react';
import { ScrollView } from 'react-native';

function Component() {
  const [momentum, setMomentum] = useState(false)

  return (
    <ScrollView
    onMomentumScrollBegin={() => setMomentum(true)}
    onEndReached={momentum === true ? console.log('end reached.') : null}
    onEndReachedThreshold={0}
    >
      <Text>Filler Text 01</Text>
      <Text>Filler Text 02</Text>
      <Text>Filler Text 03</Text>
      <Text>Filler Text 04</Text>
      <Text>Filler Text 05</Text>
    </ScrollView>
  )
}
Luisluisa answered 16/2, 2023 at 18:15 Comment(1)
When I try this I get an error that property onEndReached does not exist on ScrollViewClaudicant

© 2022 - 2024 — McMap. All rights reserved.