Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Detect bottom of page to fetch more data in react

I've try several code from several resources to detect if the user already reach the bottom of the page then do fetch more data. Its working but the function always fetch the data whenever I scroll, not whenever the user reach the bottom. Here is my code:

import React, { Component } from 'react';

class DataView extends Component {
  constructor(props) {
    super(props);

    this.state = {
      displayedData: [],
      initialData: [],
      loadingState: false,
    };

    this.fetchMoreData = this.fetchMoreData.bind(this);
    this.handleScroll = this.handleScroll.bind(this);
  }

  componentDidMount() {
    window.addEventListener('scroll', this.handleScroll, true);
  }

  componentWillReceiveProps(props) {
    const initialData = props.initialData;

    this.setState({
      initialData,
      displayedData: initialData.slice(0, 3),
    });
  }

  componentWillUnmount() {
    window.removeEventListener('scroll', this.handleScroll, true);
  }

  fetchMoreData() {
    console.log('fetch');
    this.setState({ loadingState: true });

    if (this.state.displayedData.length >= this.state.initialData.length) {
      this.setState({
        loadingState: false,
      });
      return;
    }

    const size = 3;
    const initialData = this.state.initialData;
    const copiedinitialData = [...initialData];
    const prevDisplayedData = this.state.displayedData;
    const copiedPrevDisplayedData = [...prevDisplayedData];
    const copiedPrevDisplayedDataLength = copiedPrevDisplayedData.length;
    const nextItem = copiedinitialData.slice(copiedPrevDisplayedDataLength, copiedPrevDisplayedDataLength + size);
    copiedPrevDisplayedData.push(...nextItem);

    setTimeout(() => {
      this.setState({
        displayedData: copiedPrevDisplayedData,
      });
    }, 2000);
    // use set timeout because fetching data still use mock data (not hitting API)
  }

  handleScroll() {
    // FIRST TRIAL
    // const scrollTop = document.getElementById('isScrolling').scrollTop;
    // const clientHeight = document.getElementById('isScrolling').clientHeight;
    // const scrollHeight = document.getElementById('react-element').scrollHeight; // the id is written in another component as the outer element/wrapper

    // console.log('============= ### =============');
    // console.log('scrollTop: ', scrollTop);
    // console.log('clientHeight: ', clientHeight);
    // console.log('scrollHeight: ', scrollHeight);
    // console.log('scrollHeight2: ', scrollHeight2);
    // console.log('============= ### =============');
    // if (scrollTop + clientHeight >= scrollHeight) {
    //  this.fetchMoreData();
    // }
    // above working but always fetch when scrolled, not when user reach bottom

    // SECOND TRIAL
    // const lastDiv = document.querySelector('#isScrolling > div:last-child');
    // const lastDivOffset = lastDiv.offsetTop + lastDiv.clientHeight;
    // const pageOffset = window.pageYOffset + window.innerHeight;
    // console.log('============= ### =============');
    // console.log('lastDivOffset: ', lastDivOffset);
    // console.log('pageOffset: ', pageOffset);
    // console.log('clientHeight: ', document.getElementById('isScrolling').clientHeight);
    // console.log('scrollHeight: ', document.getElementById('isScrolling').scrollHeight);
    // console.log('lastDivOffsetTop: ', lastDiv.offsetTop);
    // console.log((pageOffset / lastDivOffset) * 100);
    // console.log('============= ### =============');

    // const ratio = (pageOffset / lastDivOffset) * 100;
    // let scenario = ratio < 60;
    // let scenario = pageOffset > lastDivOffset - 10;
    // if (scenario) {
    //   this.fetchMoreData();
    // }
    // both of the scenario behave like the first trial

    console.log('============= ### =============');
    console.log('win screen: ', window.screen);
    console.log('win screenTop: ', window.screenTop);
    console.log('win screenLeft: ', window.screenLeft);
    console.log('win screenY: ', window.screenY);
    console.log('win scrollY: ', window.scrollY);
    console.log('win innerHeight: ', window.innerHeight); // always 580
    console.log('win pageYOffset: ', window.pageYOffset);
    console.log('docEl clientHeight: ', document.documentElement.clientHeight);
    console.log('docEl clientLeft: ', document.documentElement.clientLeft);
    console.log('docEl clientTop: ', document.documentElement.clientTop);
    console.log('docEl offsetHeight: ', document.documentElement.offsetHeight);
    console.log('docEl scrollHeight: ', document.documentElement.scrollHeight);
    console.log('docEl scrollLeft: ', document.documentElement.scrollLeft);
    console.log('docEl screenTop: ', document.documentElement.scrollTop);
    console.log('docBody clientHeight: ', document.body.clientHeight);
    console.log('docBody clientLeft: ', document.body.clientLeft);
    console.log('docBody clientTop: ', document.body.clientTop);
    console.log('docBody offsetHeight: ', document.body.offsetHeight);
    console.log('docBody scrollHeight: ', document.body.scrollHeight);
    console.log('docBody scrollLeft: ', document.body.scrollLeft);
    console.log('docBody screenTop: ', document.body.scrollTop);
    console.log('docId scrollHeight: ', document.getElementById('isScrolling').scrollHeight); // ==> A
    console.log('docId screenLeft: ', document.getElementById('isScrolling').scrollLeft);
    console.log('docId scrollTop: ', document.getElementById('isScrolling').scrollTop);
    console.log('docId clientHeight: ', document.getElementById('isScrolling').clientHeight); // ==> B
    console.log('docId offsetHeight: ', document.getElementById('isScrolling').offsetHeight); // ==> C
    // the A value is always 20px greater than B and C. B and C is always same value. The others console.log result is zero
    console.log('============= ### =============');

    if ((window.scrollY + window.innerHeight) >= document.body.scrollHeight) {
      // this if statement also behave like the first trial
      this.fetchMoreData();
    }
  }

  render() {
    const displayedData = this.state.displayedData.map((item, index) => {
      const itemIndex = index;
      const dataName = item.dataName;

      return <div key={itemIndex} style={{ marginBottom: 20 }}>
        {dataName}
             </div>;
    });

    return (
      <div>
        <div className="class">
          <div className="name">
            {/* {another code} */}
          </div>
          <div className="classes">
            {/* {another code} */}
          </div>
          <div id="isScrolling">
            {displayedData}
            {this.state.loadingState
              ? <div> Loading ....</div>
              : <div> No more data available</div>
            }
          </div>
        </div>
      </div>
    );
  }
}
export default DataView;

My objective: when user at certain px from the bottom, this.fetchMoreData will fired. Any help would be very helpful.

like image 640
Akza Avatar asked Feb 03 '19 23:02

Akza


People also ask

How do you find the end of the page in React?

To detect when a user scrolls to bottom of div with React, we can check if the sum of the scrollTop and clientHeight properties of a scrollable element is equal to the scrollHeight property of the same element.

How do you detect if browser window is scrolled to bottom in React?

An even simpler way to do it is with scrollHeight, scrollTop, and clientHeight. Subtract the scrolled height from the total scrollable height. If this is equal to the visible area, you've reached the bottom! In react, just add an onScroll listener to the scrollable element, and use event.


1 Answers

Put this in your componentWillMount()

window.addEventListener('scroll', function() {
   if ((window.innerHeight + window.scrollY) >= document.body.offsetHeight) {
      console.log("you're at the bottom of the page");
      // Show loading spinner and make fetch request to api
   }
});

Don't forget to kill your event Listeners in componentWillUnmount() to prevent memory leaks.

like image 170
GifCo Avatar answered Oct 03 '22 07:10

GifCo