最近在做一个功能,就是上拉加载下一页,用的是react搭建前端视图,以下是我的做法和遇到的相关问题及解决办法:
案例一:回到顶部
class Home extends Component {
consrcutor(props) {
super(props);
this.state={
showScroll: false
}
}
componentDidMount() {
this.bindEvent();
}
componentWillUnmount() {
window.removeEventListener("scoll", this.props.toggleTopShow);
}
bindEvent = () => {
window.addEventListener("scroll", this.props.toggleTopShow);
};
toggleTopShow = () => {
let showScroll = '';
if (document.documentElement.scrollTop > 100) {
showScroll = true;
} else {
showScroll = false;
}
this.setState({
showScroll
});
}
handleScrollTop = () => {
window.scrollTo(0, 0);
};
render() {
return (
<div>
{this.state.showScroll ? (
<BackTop onClick={this.handleScrollTop}>
<i className="iconfont ic-backtop"></i>
</BackTop>
) : null}
</div>
);
}
}
案例二:上拉加载下一页
页面布局说明:页面:banner+nav+content(页面高度100%,除了banner+nav固定外,剩余高度都是content,其content在其区域内随着内容增多是可以滚动的)
根据上面说明知道:页面整体不滚动,只是页面的局部区域滚动,所以不能将scroll绑定到window上而是content上,如下:
class Pool extends PureComponent {
constructor(props) {
super(props);
this.state = {
PullLoadingTip: ""
};
this.isLock = true;
this.PoolCon = React.createRef();
this.ParentCon = React.createRef();
this.scrollToBottom = this.scrollToBottom.bind(this);
}
componentDidMount() {
this.ParentCon.current.addEventListener("scroll", this.scrollToBottom);
}
componentWillUnmount() {
this.ParentCon.current.removeEventListener("scroll", this.scrollToBottom);
}
scrollToBottom(event) {
event.stopPropagation();
const parent = this.ParentCon.current; //event.target;
let scrolltop = parent.scrollTop; //content上部滚动出的高度
let height = Zepto(parent).height(); //content的固定高度
let scrollheight = this.PoolCon.current.scrollHeight; //content内部内容的高度
if (scrollheight <= scrolltop + height) {
console.log("到底了");
if (Math.ceil(this.TOTAL / LIMIT) >= this.PAGE + 1) { //有下一页才执行可以加载
this.setState(
{
isShowPullLoading: true
},
() => {
if (this.isLock) { //请求下一页过程中不在发送请求
this.getData();
}
}
);
}
}
}
getData(flag) {
this.isLock = false
//发送请求获取下一页数据,成功后this.isLock改为true
}
render() {
return (
<article className="content">
<section ref={this.PoolCon}>
{/*content列表内容*/}
{this.state.isShowPullLoading ? (
<p className="pull-loading">正在加载...</p>
) : (
""
)}
</section>
</article>
);
}
}
问题:上面两个案例均在componentDidMount()中均绑定了scroll事件,但是绑定到window上功能正常,绑定到某个元素上只有第一次有效,之后就不会再出发scroll事件。
解决:在元素上直接添加onScroll事件,而不是在componentDidMount()中绑定。修改如下:
//去掉this.ParentCon = React.createRef();
//<article className="content" onScroll={this.scrollToBottom}>...</article>
//scrollToBottom中:const parent = event.target;
案例三:移动端上实现上拉加载下一页
网上有很多插件如iscroll、better-scroll、http://www.htmleaf.com/jQuery/jquery-tools/201905275664.html等,可以看看(未实测),但是我的需求要求不高,没有搜索react相关插件, 就是用上面的原理实现的。
移动端一般不监听scroll事件,所以将绑定的事件改为touchstart、touchmove、touchend
touchstart事件:当手指触摸屏幕时候触发,即使已经有一个手指放在屏幕上也会触发。
touchmove事件:当手指在屏幕上滑动的时候连续地触发。在这个事件发生期间,调用preventDefault()事件可以阻止滚动。
touchend事件:当手指从屏幕上离开的时候触发。
touchcancel事件:当系统停止跟踪触摸的时候触发。关于这个事件的确切出发时间,文档中并没有具体说明,咱们只能去猜测了。
问题:移动端touch相关事件是绑定到元素上,还是生命周期componentDidMount函数中?
实际测试onTouchmove、onTouchend直接绑定到元素上,可以触发,但是很难触发;绑定到componentDidMount上功能正常,即直接在componentDidMount中将touch相关事件绑定到content上。
//再把 this.PoolCon = React.createRef();加回来
// <article className="content" ref={this.ParentCon}>...</article>
componentDidMount() {
this.ParentCon.current.addEventListener("touchmove", () => this.scrollToBottom(event, 1), { passive: false });
this.ParentCon.current.addEventListener("touchend", () => this.scrollToBottom(event, 2));
}
componentWillUnmount() {
this.ParentCon.current.removeEventListener("touchmove", () => this.scrollToBottom(event, 1));
this.ParentCon.current.removeEventListener("touchend", () => this.scrollToBottom(event, 2));
}
scrollToBottom(event, type) {
event.stopPropagation();
const parent = this.ParentCon.current; //event.target;
let scrolltop = parent.scrollTop;
let height = Zepto(parent).height();
let scrollheight = this.PoolCon.current.scrollHeight;
if (scrollheight <= scrolltop + height) {
if (Math.ceil(this.TOTAL / LIMIT) >= this.PAGE + 1) {
if (1 === type) {
this.setState({
PullLoadingTip: "释放立即加载..."
});
} else if (2 === type) {
this.setState(
{
PullLoadingTip: "正在加载..."
},
() => {
if (this.isLock) {
this.getData();
}
}
);
}
}
}
}
案例四:移动端实现下拉刷新
因为没有需求,所以不是react版本
<main class="parent">
<p class="refreshText"></p>
<ul id="refreshContainer">
...
<li>111</li>
...
</ul>
</main>
window.onload = function(){
//1.获取到列表的dom,刷新显示部分的dom,列表父容器的dom
let container = document.querySelector('#refreshContainer');
let refreshText = document.querySelector('.refreshText');
let parent = document.querySelector('.parent');
//2.定义一些需要常用的变量
let startY = 0;//手指触摸最开始的Y坐标
let endY = 0;//手指结束触摸时的Y坐标
//3.给列表dom监听touchstart事件,得到起始位置的Y坐标
parent.addEventListener('touchstart',function(e){
startY = e.touches[0].pageY;
});
//4.给列表dom监听touchmove事件,当移动到一定程度需要显示上面的文字
parent.addEventListener('touchmove',function (e) {
if(isTop() && (e.touches[0].pageY-startY) > 0){
console.log('下拉了');
refreshText.style.height = "50px";
parent.style.transform = "translateY(50px)";
parent.style.transition = "all ease 0.5s";
refreshText.innerHTML = "释放立即刷新...";
}
});
//5.给列表dom监听touchend事件,此时说明用户已经松开了手指,应该进行异步操作了
parent.addEventListener('touchend',function (e) {
if(isTop()){
refreshText.innerHTML = "正在刷新...";
setTimeout(function(){
parent.style.transform = "translateY(0)";
console.log('成功刷新');
},2000)
}
return;
})
function isTop(){
var t = document.documentElement.scrollTop||document.body.scrollTop;
return t === 0 ? true : false;
}
}
以上就是自己的做法,有问题可以留言,共同成长。
求告知好用的react相关组件库