检测页面底部以在反应中获取更多数据
Posted
技术标签:
【中文标题】检测页面底部以在反应中获取更多数据【英文标题】:Detect bottom of page to fetch more data in react 【发布时间】:2019-06-27 18:05:42 【问题描述】:我尝试了几个资源中的几个代码来检测用户是否已经到达页面底部,然后获取更多数据。它的工作但功能总是在我滚动时获取数据,而不是每当用户到达底部时。这是我的代码:
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;
我的目标:当用户在底部的某个像素处时,this.fetchMoreData 将被触发。任何帮助都会非常有帮助。
【问题讨论】:
对不起,我现在看到你确实尝试了同样的事情,它应该可以工作。您确定您的页面不会太短并且加载时滚动条很少或没有滚动条吗?你说 innerHeight 总是 580。那真的很小 它加载时滚动条很小(除非我增加了我想在第一次加载时显示的初始数据)。 innerHeight 很小,因为我尝试使用 iPhone 5S 的屏幕尺寸来查看应用程序。但是,我不明白为什么每次滚动时总是触发 fetch 函数,而不是检查距离。例如,当我(轻轻地)滚动 5 次时。然后我的所有数据都被提取了,但是,我离顶部不远,底部离我当前的视图还很远 您是否尝试过将 document.body.scrollHeight 换成 document.body.offsetHeight ?它们有点相同,但处理填充和边框的方式不同。还可以尝试使用计算结果在 if 语句中登录控制台,这样您就可以看到出了什么问题。 【参考方案1】:把这个放在你的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
);
不要忘记杀死componentWillUnmount()
中的事件监听器以防止内存泄漏。
【讨论】:
【参考方案2】:constructor(props)
super(props);
this.state =
height: window.innerHeight,
message: 'not at bottom'
;
this.handleScroll = this.handleScroll.bind(this);
handleScroll()
const windowHeight = "innerHeight" in window ? window.innerHeight : document.documentElement.offsetHeight;
const body = document.body;
const html = document.documentElement;
const docHeight = Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight);
const windowBottom = windowHeight + window.pageYOffset;
if (windowBottom >= docHeight)
this.setState(
message: 'bottom reached'
);
else
this.setState(
message: 'not at bottom'
);
componentDidMount()
window.addEventListener("scroll", this.handleScroll);
componentWillUnmount()
window.removeEventListener("scroll", this.handleScroll);
【讨论】:
【参考方案3】:已编辑请注意这是一个反应钩子解决方案
useEffect(() =>
const scrolling_function = () =>
if((window.innerHeight + window.scrollY) >= document.body.offsetHeight-10)
console.log("fetching more.........")
window.removeEventListener('scroll',scrolling_function)
window.addEventListener('scroll', scrolling_function);
, [target_data])
【讨论】:
这行不通,因为它表示为 React hook,与 OP 中使用的 Component 类不兼容。 这对我不起作用,因为在这个钩子中,其他有状态的对象变为空【参考方案4】: useEffect(() =>
const onScroll = function ()
if (window.innerHeight + window.scrollY >= document.body.offsetHeight)
console.log("you're at the bottom of the page")
window.addEventListener('scroll', onScroll)
return () => window.removeEventListener('scroll', onScroll)
, [])
【讨论】:
以上是关于检测页面底部以在反应中获取更多数据的主要内容,如果未能解决你的问题,请参考以下文章