title: Detect bottom of page to fetch more data in react
body: <p>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:</p>
  
  <pre><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 &gt;= 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(() =&gt; {
        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 &gt;= scrollHeight) {
      //  this.fetchMoreData();
      // }
      // above working but always fetch when scrolled, not when user reach bottom
  
      // SECOND TRIAL
      // const lastDiv = document.querySelector('#isScrolling &gt; 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 &lt; 60;
      // let scenario = pageOffset &gt; 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); // ==&gt; 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); // ==&gt; B
      console.log('docId offsetHeight: ', document.getElementById('isScrolling').offsetHeight); // ==&gt; 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) &gt;= document.body.scrollHeight) {
        // this if statement also behave like the first trial
        this.fetchMoreData();
      }
    }
  
    render() {
      const displayedData = this.state.displayedData.map((item, index) =&gt; {
        const itemIndex = index;
        const dataName = item.dataName;
  
        return &lt;div key={itemIndex} style={{ marginBottom: 20 }}&gt;
          {dataName}
               &lt;/div&gt;;
      });
  
      return (
        &lt;div&gt;
          &lt;div className="class"&gt;
            &lt;div className="name"&gt;
              {/* {another code} */}
            &lt;/div&gt;
            &lt;div className="classes"&gt;
              {/* {another code} */}
            &lt;/div&gt;
            &lt;div id="isScrolling"&gt;
              {displayedData}
              {this.state.loadingState
                ? &lt;div&gt; Loading ....&lt;/div&gt;
                : &lt;div&gt; No more data available&lt;/div&gt;
              }
            &lt;/div&gt;
          &lt;/div&gt;
        &lt;/div&gt;
      );
    }
  }
  export default DataView;
  </code></pre>
  
  <p>My objective: when user at certain px from the bottom, this.fetchMoreData will fired. Any help would be very helpful.</p>