ListLoader 移动端上拉加载更多组件

import React, { ReactElement, useState, useEffect, useMemo } from ‘react’
import { getPageDom } from ‘@client/lib/getPageDom’

interface ListLoaderProps<T> {
  // 每次传入的某页列表
  singlePageList: T[]

  // 是否是连续的,即拼接还是覆盖
  continuously?: boolean

  // 是否正在加载中
  loading: boolean

  // 是否已加载完所有
  loadedAll: boolean | ((stateList: T[]) => boolean)

  // 预加载触发距离,即距离真正的底部多少像素可以触发到达底部事件
  preloadingDistance?: number

  // 到达底部执行的事件
  onEndReached: () => void

  // 在列表加载后执行的事件
  afterLoaded?: (stateList: T[]) => void

  // 将内部控制的列表往下传
  children: (stateList: T[]) => ReactElement | ReactElement[]
}

export function ListLoader<T> (props: ListLoaderProps<T>) {
  const { loading, singlePageList = [], continuously = true, preloadingDistance = 150, onEndReached, afterLoaded, children } = props

  // 内部管理的列表
  const [stateList, setStateList] = useState(singlePageList)

  // 只有 singlePageList 更新时才更新 stateList
  useEffect(() => {
    const newStateList = continuously ? stateList.concat(singlePageList) : singlePageList
    setStateList(newStateList)
    afterLoaded && afterLoaded(newStateList)
  }, [singlePageList])

  // 是否已加载完所有
  const loadedAll = useMemo(() => {
    const { loadedAll } = props
    if (typeof loadedAll === 'function') return loadedAll(stateList)
    else return !!loadedAll
  }, [props.loadedAll])

  // 定时器监听是否需要加载
  useEffect(() => {
    function checkIfLoadMore () {
      // 如果正在加载,或者已加载完,退出函数
      if (loading || loadedAll) return

      const scrollTop
        = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop

      if (scrollTop + window.innerHeight + preloadingDistance >= getPageDom().offsetHeight) onEndReached()
    }

    const timer = window.setInterval(checkIfLoadMore, 500)
    return () => window.clearInterval(timer)
  }, [loading, loadedAll, preloadingDistance, onEndReached])

  return (
    <>
      {children(stateList)}
      <div className="coll-loadmore-mb">
        {
          // loading 样式,loading 结束时如果还没加载完也先不移除,体验上会好点
          (loading || !loadedAll) ? (
            <>
              <div className="coll-loadmore-mb-icon">
                <div className="c-loading">
                  <div className="c-loading-inner">
                    <div className="one" />
                    <div className="two" />
                    <div className="three" />
                  </div>
                </div>
              </div>
              <div className="coll-loadmore-mb-text">正在加载中...</div>
            </>
          ) : (
          // 已加载完样式,如果列表为空会有空状态渲染,不需要到达底部的展示
            stateList.length > 0 && (
              <div className="coll-loadmore-mb-text" style={{ color: '#999' }}>已经到达底部了</div>
            )
          )
        }
      </div>
    </>
  )
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值