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.
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With