需要传入的参数:
必传参数:
curPage(当前页数)
totalPage(总页数)
callback(滚动条加载事件)
prevPage(点击上一页加载事件)
nextPage(点击下一页加载事件)
选传参数:
Interval(分页间隔)状态:
loaded: true 是否加载完成 true加载完成 false加载中
loadingText: “加载中”
btnShow: falseloaded:true文案显示 gif+加载中 false文案为空
btnShow: false 分页按钮不显示
scroll 事件
清除定时器
在componentDidMount的时候给window添加组件的滚动事件
在componentWillUnmount的时候移除window的滚动事件
1、
获取加载更多/加载中距离屏幕顶部的高度const wrapper = this.wrapperRef.current let wrapperTop if (wrapper) { wrapperTop = wrapper.getBoundingClientRect().top }
获取屏幕高度
const windowHeight = window.screen.height
① 判断wrapperTop < windowHeight
,若为true
表示可以加载了
再判断页数条件
const { curPage, totalPage, interval } = this.state
②curPage < totalPage
当前页数小于总页数
③curPage % interval !== 0
当前页数不是分页间隔的倍数
④loaded
加载完成状态
四个条件都满足后
改变状态loaded: false
loadingText: "加载中"
btnShow: false
再触发数据请求的事件 loadMore
loadMore事件
!this.state.loaded
加载中的状态时
触发外面传给组件的回调函数callback
不管请求成功失败,都改变加载状态
当前页数等于总页数时loadingText: ""
,否则loadingText: "加载更多"
btnShow: true, loaded: true
prevPage
点击上一页触发事件, 数据处理完成后,回到顶部
nextPage
点击下一页触发事件, 数据处理完成后,回到顶部
componentDidUpdate处理
const { curPage, totalPage } = this.state if (curPage === totalPage) { this.setState({ loadingText: "" }) } // 上面部分可能重复
加载更多/加载中 ref={this.wrapperRef}
当总页数等于0(没有数据)
当前页数是分页间隔的倍数 并且 总页数大于分页间隔
当前页数等于总页数 并且 当前页数大于总页数
满足上述三个条件中的任一 一个
加载更多/加载中 隐藏
根据loaded的状态 选择显示文案状态
加载更多/gif + 加载中
分页按钮
分页按钮是否显示 根据页数判断
totalPage >= interval
总页数大于分页间隔
(curPage === totalPage || curPage % interval === 0)
当前页数等于总页数 或者 当前页数是分页间隔的倍数
totalPage > 0
总页数大于0(有数据)
同时满足上述条件 按钮显示
使用
//包裹需要上拉加载的区域
// 传入滚动加载数据请求事件callback
// 点击上一页数据请求事件prevPage
// 点击下一页数据请求事件nextPage
// 分页间隔interval
// 总页数totalPage
// 当前页数curPage
<PullOnloading
callback={this.getNewData}
prevPage={this.prevPage}
nextPage={this.nextPage}
interval={interval}
totalPage={totalPage}
curPage={curPage}
loading={loading}
>
{!loading ? (
emptyStatus ? (
this.blankPage
) : totalPage > 0 ? (
<CommonBody dataList={dataList} style={style} />
) : (
this.getNoLike
)
) : (
<Blank />
)}
</PullOnloading>
代码
// pull-onloading
import React from "react"
import styles from "./index.module.less"
import classNames from "classnames"
import propTypes from "prop-types"
import loadingGif from "../../static/img/loading.gif"
// 传入参数
// curPage(当前页) 必传,
// totalPage(总页数) 必传,
// interval(分页间隔),
// callback(滚动加载方法) 必传,
// prevPage(上一页加载方法) 必传,
// nextPage(下一页加载方法) 必传
class PullOnloading extends React.PureComponent {
constructor(props) {
super(props)
this.state = {
loadingText: "加载中",
loaded: false,
btnShow: false,
curPage: 0,
totalPage: 0,
interval: 10,
callback: null,
prevPage: null,
nextPage: null
}
this.timeCount = null
this.outterRef = React.createRef()
this.innerRef = React.createRef()
this.wrapperRef = React.createRef()
}
static getDerivedStateFromProps(prevProps, prevState) {
// 1.外面传进当前页数时,映射props
// 2.外面传进分页间隔数 interval时, 映射props
// 3.state中必传函数不存在时,映射props
// 4.外面传进分页间隔数 totalPage, 映射props
// 外界后面会改变的只有curPage
if (
prevProps.interval !== prevState.interval ||
prevProps.curPage !== prevState.curPage ||
prevProps.totalPage !== prevState.totalPage ||
!prevState.callback ||
!prevState.prevPage ||
!prevState.nextPage
) {
return prevProps
}
// 4.其余时候 自身state发生改变 映射state 如:loadingText, loaded, btnShow
if (prevState.interval) {
return prevState
}
return null
}
readyRequest = () => {
const { totalPage, curPage, interval } = this.state
const innerBox = this.innerRef.current
const wrapper = this.wrapperRef.current
const wrapperTop = wrapper.getBoundingClientRect().top
const windowHeight = window.screen.height
if (
innerBox.clientHeight < windowHeight &&
wrapperTop < windowHeight &&
curPage < totalPage &&
curPage % interval !== 0 &&
this.state.loaded
) {
this.timeCount = setTimeout(this.loadMore, 200)
}
}
componentDidMount() {
const { totalPage, curPage, interval } = this.state
if (curPage === totalPage) {
this.setState({ loadingText: "", loaded: true })
} else {
this.setState({ loadingText: "加载更多", loaded: true })
}
window.addEventListener("scroll", this.scroll, false)
}
componentWillUnmount() {
window.removeEventListener("scroll", this.scroll, false)
clearTimeout(this.timeCount)
}
componentDidUpdate() {
const { curPage, totalPage, interval } = this.state
if (curPage === totalPage) {
this.setState({ loadingText: "" })
}
this.readyRequest()
//loadingTextHidden
if (totalPage <= interval && curPage === totalPage) {
this.setState({ loadingTextHidden: true })
}
}
loadMore = async () => {
if (!this.state.loaded) {
await this.props.callback().finally(() => {
const { totalPage, curPage } = this.state
const loadingText = curPage === totalPage ? "" : "加载更多"
this.setState({ btnShow: true, loaded: true, loadingText })
})
}
}
scroll = async () => {
if (this.timeCount) clearTimeout(this.timeCount)
const wrapper = this.wrapperRef.current
const { loaded } = this.state
let wrapperTop
if (wrapper) {
wrapperTop = wrapper.getBoundingClientRect().top
}
const windowHeight = window.screen.height
const { curPage, totalPage, interval } = this.state
if (curPage >= interval && curPage % interval === 0) {
return
}
if (
wrapperTop < windowHeight &&
curPage < totalPage &&
curPage % interval !== 0 &&
loaded
) {
await this.setState({
loaded: false,
loadingText: "加载中",
btnShow: false
})
this.loadMore()
}
}
backTop = () => {
// 需要上拉加载的区域的顶部
const outterRef = this.outterRef.current
document.documentElement.scrollTop = outterRef.offsetTop
document.body.scrollTop = outterRef.offsetTop
}
prevPage = async () => {
await this.props.prevPage()
this.backTop()
}
nextPage = async () => {
await this.props.nextPage()
this.backTop()
}
get loadingGif() {
return (
<div className={styles.loadingGif}>
<img className={styles.loading} src={loadingGif} alt="" />
<span className={styles.loadingText}>加载中</span>
</div>
)
}
render() {
const {
loadingText,
curPage,
btnShow,
totalPage,
interval,
loaded
} = this.state
return (
<div className={styles.outerBoxLoad} ref={this.outterRef}>
<div className={classNames(styles.innerBoxLoad)} ref={this.innerRef}>
<div className={styles.onloadingChildrenBoxLoad}>
{this.props.children}
</div>
<div
className={classNames(
styles.loadMore,
(totalPage === 0 ||
(curPage % interval === 0 && totalPage > interval) ||
(curPage === totalPage && curPage > interval)) &&
styles.loadMoreHidden
)}
ref={this.wrapperRef}
>
{!loaded ? (
<React.Fragment>
<img className={styles.loading} src={loadingGif} alt="" />
<span className={styles.loadingText}>{loadingText}</span>
</React.Fragment>
) : (
<span className={styles.loadingText}>{loadingText}</span>
)}
</div>
{ totalPage >= interval &&
(curPage === totalPage || curPage % interval === 0) &&
totalPage > 0 && (
<div className={classNames(styles.pullBtnBoxLoad)}>
<button
className={classNames(
styles.pullBtnLoad,
curPage === interval && styles.hiddenBtnLoad
)}
onClick={this.prevPage}
>
上一页
</button>
<button
className={classNames(
styles.pullBtnLoad,
curPage === totalPage && styles.hiddenBtnLoad
)}
onClick={this.nextPage}
>
下一页
</button>
</div>
)}
</div>
</div>
)
}
}
PullOnloading.propTypes = {
callback: propTypes.func,
prevPage: propTypes.func,
nextPage: propTypes.func,
interval: propTypes.number,
totalPage: propTypes.number,
curPage: propTypes.number
}
export default PullOnloading