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>
</>
)
}