上拉加载 分页处理

需要传入的参数:
必传参数:
curPage(当前页数)
totalPage(总页数)
callback(滚动条加载事件)
prevPage(点击上一页加载事件)
nextPage(点击下一页加载事件)
选传参数:
Interval(分页间隔)

状态:
loaded: true 是否加载完成 true加载完成 false加载中
loadingText: “加载中”
btnShow: false

loaded: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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值