上拉加载 自用

使用
//使用上拉加载组件页面的state
this.state = {
      style: "comment", 
      dataList: [], // 数据列表
      interval: 5, // 分页间隔
      params: {  // 数据请求参数
        page: 1,
        pageSize: 5,
        action: "comment"
      },
      leaveState: {} // 离开时缓存状态
    }
//使用上拉加载组件页面的方法  需要参数接收数据列表
 setData = (dataList, initial) => {
    if (initial && !dataList.length) {
    // 首次数据请求 并且 没有数据 走空页面
      this.setState({ noReview: true })
    } else {
      this.setState({ noReview: false, dataList })
    }
  }


  setLeaveState = leaveState => {
    this.setState({ leaveState })
  }
// 使用上拉加载组件页面的render
// 需要用到上拉加载的区域 用PullOnloading包裹起来
// callback 获取数据的方法  api 不走redux
// interval 分页间隔
// params 请求数据的参数(初始参数)
// setData 获取上拉加载的数据列表并设置到state的的方法
// isSigned 是否登录 通过高阶组件获取 withAuth -> withUser
// setLeaveState 设置离开时获取缓存数据的凭证
<div className={styles.messageOutBox}>
    <Nav value={"评论"} />
    <PullOnloading
      callback={getMessages}
      interval={interval}
      params={params}
      setData={this.setData}
      isSigned={this.props.isSigned}
      setLeaveState={this.setLeaveState}
    >
      <CommonBody
        dataList={dataList}
        style={style}
        state={{ source: leaveState }}
      />
    </PullOnloading>
</div>
上拉加载组件

状态预设:
loaded: false -> 请求数据 false请求完成 true请求中 取名有问题
loading: true -> 第一次加载状态 初始为true 只要有一次请求完成即为false
emptyStatus: false -> 请求报错改为true 显示404页面
btnShow: false -> 按钮是否显示 加载状态时不显示 加载完成才能显示

方法
scroll = () => {
    const wrapper = this.wrapperRef.current //加载文字的元素
    const wrapperTop = wrapper.getBoundingClientRect().top 
    // 获取加载文字元素距离屏幕顶部的距离
    const windowHeight = window.screen.height
    // 屏幕的高度
    const { timeCount, curPage, totalPage, interval } = this.state
    // 清楚定时器
    if (timeCount) clearTimeout(timeCount)
    // 加载文字出现在屏幕中
    // 当前页数小于总页数
    // 当前页数不是分页间隔的倍数
    if (
      wrapperTop < windowHeight &&
      curPage < totalPage &&
      curPage % interval !== 0
    ) {
      this.setState({ loadingText: "加载中" })
      // 满足条件后 触发数据请求 loadMore
      this.setState({
        timeCount: setTimeout(this.loadMore, 500)
      })
    }
  }
loadMore = async type => {
    const { callback, setData } = this.props
    const { curPage, loaded, dataList, params, interval } = this.state
    if (!loaded) {
      await this.setState(
        {
          loaded: true,
          btnShow: false
        },
        () => {
          if (!type || type === "initial") {
            this.setState({ curPage: curPage + 1 })
          }
        }
      )
      this.timer = setTimeout(() => {
        params.page = this.state.curPage
        callback(params)
          .then(res => {
            this.setState(
              {
                dataList: [...dataList, ...res.list],
                loaded: false,
                params
              },
              async () => {
                if (!this.state.dataList.length) {
                  this.setState({ noData: true })
                }
                const totalPage = Math.ceil(res.total / params.pageSize)
                if (type === "initial") {
                  // 首次数据请求
                  await setData(this.state.dataList, "initial")
                } else {
                  await setData(this.state.dataList)
                }
                this.setState({
                  loading: false,
                  emptyStatus: false,
                  btnShow: true,
                  totalPage
                })
                // 内容未撑满一屏 还有内容 继续发送请求
                if (this.state.dataList.length) {
                  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 - 20 < windowHeight &&
                    this.state.curPage < totalPage
                  ) {
                    if (this.state.curPage % interval == 0) return
                    setTimeout(() => {
                      this.loadMore()
                    }, 500)
                  }
                }
              }
            )
          })
          .catch(e => {
            console.log(e)
            this.setState({ loading: false, emptyStatus: true })
          })
      }, 200)
    }
  }

  // 点击上一页处理
  prevPage = async () => {
    const { setData, interval } = this.props
    const { curPage } = this.state
    // 获取点击上一页之后的页数  
    // 例如:间隔为5  当前页数为6 
    // 将6最大化到所在分页的最大页数 10 (不满分页间隔的倍数都处理成分页间隔的倍数)
    // 再减去分页间隔数的两倍
    const pageNum = Math.ceil(curPage / interval) * interval - 2 * interval
    // 下一条要请求的数据就是所得的pageNum再加1
    await this.setState({
      curPage: pageNum + 1,
      dataList: [],
      btnShow: false,
      loadingText: "加载中"
    })
    // 清空数据列表  显示加载中文字
    await setData(this.state.dataList)
    // 再发送请求
    setTimeout(() => {
      this.loadMore("prevPage")
    }, 500)
  }

  nextPage = async () => {
  	// 点击下一页处理
    const { setData } = this.props
    const { curPage } = this.state
    await this.setState({
      dataList: [],
      btnShow: false,
      loadingText: "加载中",
      curPage: curPage + 1
    })
    // 清空数据列表  继续请求下一条数据
    await setData(this.state.dataList)
    setTimeout(() => {
      this.loadMore("nextPage")
    }, 500)
  }
加载中显示

隐藏:
当前页数是分页间隔的倍数 或者 当前页数等于总页数

 <div
  className={classNames(
     styles.loadMoreLoad,
     (curPage % interval == 0 || curPage == totalPage) &&
       styles.loadMoreHidden
   )}
   ref={this.wrapperRef}
 >
   <img className={styles.loading} src={loadingGif} alt="" />
   {loadingText}
 </div>
上下页按钮
// btnShow: true 数据请求完成
// 总页数大于分页间隔数
// 当前页数等于总页数 或者 当前页数是分页间隔的倍数
// 总页数大于零
// 同时满足上述条件时 按钮出现

// 上一页按钮隐藏  当前页数等于分页间隔时 
// 即第一页到底了 要去第二页了  没有上一页
// 下一页按钮隐藏 当前页数等于总页数
// 即最后一页了 没有下一页
{btnShow &&
 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>
)}
页面缓存处理

1、 import withCache from "../../containers/cache"
2、 export default compose(withCache)(PullOnloading)
3、

componentWillUnmount() {
   window.removeEventListener("scroll", this.scroll, false)
   clearTimeout(this.state.timeCount)
   // 记录
   if (this.props.isSigned) {
     this.props.setCacheData({
       // TODO: 数据兼容: 旧代码没有将type放在params里做处理
       // 存储在session storage的参数是扁平的
       params: {
         params: this.state.params,
         dataList: this.state.dataList,
         curPage: this.state.curPage,
         btnShow: this.state.btnShow,
         totalPage: this.state.totalPage,
         loadingText: this.state.loadingText,
         interval: this.props.interval
       }
     })
   }
 }	

4、constructor里面
const cached = props.getCacheData()

if (cached) {
     this.cached = true
     this.state = {
       timeCount: null,
       loaded: false,
       loadingText: cached.params.loadingText,
       curPage: cached.params.curPage,
       dataList: cached.params.dataList,
       totalPage: cached.params.totalPage,
       loading: false,
       emptyStatus: false,
       btnShow: cached.params.btnShow,
       interval: cached.params.interval,
       params: cached.params.params
     }
   }

5、this.props.setLeaveState(this.props.cacheKey)将缓存凭证传给使用页面

import React from "react"
import { withRouter } from "react-router-dom"
import styles from "./index.module.less"
import classNames from "classnames"
import propTypes from "prop-types"
import Blank from "../../component/blank"
import ErrorBlank from "../blank-page"
import ERROR_IMG from "../../static/img/error-img.png"
import loadingGif from "../../static/img/loading.gif"
import withCache from "../../containers/cache"
import { compose } from "redux"

/*
<PullOnloading
            callback={getMessages} // api方法 返回Promise
            params={params} // api 参数 包括page、pageSize
            interval={3} // 分页页距  例:每三次请求换一页
            setData={this.setData} // 设置要用的数据列表
          >
          {调用的组件的内容区域}
</PullOnloading>
调用的组件
setData = dataList => {
    // console.log(dataList)
    this.setState({ dataList })
  }
 */
@withRouter
class PullOnloading extends React.PureComponent {
  constructor(props) {
    super(props)
    const cached = props.getCacheData()
    if (cached) {
      this.cached = true
      this.state = {
        timeCount: null,
        loaded: cached.params.loaded,
        loadingText: cached.params.loadingText,
        curPage: cached.params.curPage,
        dataList: cached.params.dataList,
        totalPage: cached.params.totalPage,
        loading: cached.params.loading,
        emptyStatus: cached.params.emptyStatus,
        btnShow: cached.params.btnShow,
        interval: cached.params.interval,
        params: cached.params.params,
        noData: cached.params.noData
      }
    } else {
      this.state = {
        timeCount: null,
        loaded: false,
        loading: true,
        emptyStatus: false,
        loadingText: "加载中",
        curPage: 0,
        dataList: [],
        btnShow: false,
        params: props.params,
        interval: props.interval,
        totalPage: 0,
        noData: false
      }
    }
    this.outterRef = React.createRef()
    this.innerRef = React.createRef()
    this.wrapperRef = React.createRef()
  }

  componentDidMount() {
    const cached = this.props.getCacheData()
    this.props.setLeaveState(this.props.cacheKey)
    this.props.setData(this.state.dataList)
    const { curPage, interval } = this.state
    if (curPage % interval !== 0 || curPage === 0) {
      if (!cached) {
        this.loadMore("initial")
      }
    }
    window.addEventListener("scroll", this.scroll, false)
  }

  componentWillUnmount() {
    window.removeEventListener("scroll", this.scroll, false)
    clearTimeout(this.state.timeCount)
    // 记录
    if (this.props.isSigned) {
      this.props.setCacheData({
        // TODO: 数据兼容: 旧代码没有将type放在params里做处理
        // 存储在session storage的参数是扁平的
        params: {
          params: this.state.params,
          dataList: this.state.dataList,
          curPage: this.state.curPage,
          btnShow: this.state.btnShow,
          totalPage: this.state.totalPage,
          loadingText: this.state.loadingText,
          interval: this.state.interval,
          loaded: this.state.loaded,
          loading: this.state.loading,
          emptyStatus: this.state.emptyStatus,
          noData: this.state.noData
        }
      })
    }
  }

  get blankPage() {
    return (
      <ErrorBlank
        source={"error"}
        img={ERROR_IMG}
        btn={"返回首页"}
        tips={"404,您要找的页面去火星了~~"}
      />
    )
  }

  loadMore = async type => {
    const { callback, setData } = this.props
    const { curPage, loaded, dataList, params, interval } = this.state
    if (!loaded) {
      await this.setState(
        {
          loaded: true,
          btnShow: false
        },
        () => {
          if (!type || type === "initial") {
            this.setState({ curPage: curPage + 1 })
          }
        }
      )
      this.timer = setTimeout(() => {
        params.page = this.state.curPage
        callback(params)
          .then(res => {
            this.setState(
              {
                dataList: [...dataList, ...res.list],
                loaded: false,
                params
              },
              async () => {
                if (!this.state.dataList.length) {
                  this.setState({ noData: true })
                }
                const totalPage = Math.ceil(res.total / params.pageSize)
                if (type === "initial") {
                  await setData(this.state.dataList, "initial")
                } else {
                  await setData(this.state.dataList)
                }
                this.setState({
                  loading: false,
                  emptyStatus: false,
                  btnShow: true,
                  totalPage
                })
                // 内容未撑满一屏 还有内容 继续发送请求
                if (this.state.dataList.length) {
                  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 - 20 < windowHeight &&
                    this.state.curPage < totalPage
                  ) {
                    if (this.state.curPage % interval == 0) return
                    setTimeout(() => {
                      this.loadMore()
                    }, 500)
                  }
                }
              }
            )
          })
          .catch(e => {
            console.log(e)
            this.setState({ loading: false, emptyStatus: true })
          })
      }, 200)
      // 数值为0 会出问题
    }
  }

  scroll = () => {
    const wrapper = this.wrapperRef.current
    const wrapperTop = wrapper.getBoundingClientRect().top
    const windowHeight = window.screen.height

    const { timeCount, curPage, totalPage, interval } = this.state
    if (curPage >= totalPage) return
    if (timeCount) clearTimeout(timeCount)
    if (
      wrapperTop < windowHeight &&
      curPage < totalPage &&
      curPage % interval !== 0
    ) {
      this.setState({ loadingText: "加载中" })
      this.setState({
        timeCount: setTimeout(this.loadMore, 500)
      })
    }
  }

  backTop = () => {
    const dom = document.documentElement || document.body
    dom.scrollTop = 0
  }

  prevPage = async () => {
    const { setData, interval } = this.props
    const { curPage } = this.state
    const pageNum = Math.ceil(curPage / interval) * interval - 2 * interval
    await this.setState({
      curPage: pageNum + 1,
      dataList: [],
      btnShow: false,
      loadingText: "加载中"
    })
    await setData(this.state.dataList)
    setTimeout(() => {
      this.loadMore("prevPage")
    }, 500)
  }

  nextPage = async () => {
    const { setData } = this.props
    const { curPage } = this.state
    await this.setState({
      dataList: [],
      btnShow: false,
      loadingText: "加载中",
      curPage: curPage + 1
    })
    await setData(this.state.dataList)
    setTimeout(() => {
      this.loadMore("nextPage")
    }, 5000)
  }

  render() {
    const {
      loadingText,
      curPage,
      totalPage,
      loading,
      emptyStatus,
      btnShow,
      interval,
      noData
    } = this.state
    const { background = {} } = this.props
    console.log(curPage)
    console.log(totalPage)
    return (
      <div
        className={styles.outerBoxLoad}
        ref={this.outterRef}
        style={background}
      >
        {emptyStatus ? (
          this.blankPage
        ) : (
          <div className={classNames(styles.innerBoxLoad)} ref={this.innerRef}>
            <div className={styles.onloadingChildrenBoxLoad}>
              {this.props.children}
            </div>
            {!noData && (
              <div
                className={classNames(
                  styles.loadMoreLoad,
                  (curPage % interval == 0 || curPage == totalPage) &&
                    styles.loadMoreHidden
                )}
                ref={this.wrapperRef}
              >
                <img className={styles.loading} src={loadingGif} alt="" />
                {loadingText}
              </div>
            )}
            {btnShow &&
              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>
        )}
        {loading && <Blank />}
      </div>
    )
  }
}
PullOnloading.propTypes = {
  callback: propTypes.func,
  interval: propTypes.number,
  setData: propTypes.func,
  params: propTypes.object
}
export default compose(withCache)(PullOnloading)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值