前言
下拉加载更多我想每个前端开发都遇到过,需要的时候就在网上搜,很有可能搜到的都是一些过去“老时代”的写法。前端发展的这么快,不抓紧跟上,你的代码编写只会越来越落伍。
本次文章就教你用react18+ahook+antd第三方库最简单的方式去实现一个下拉加载更多。
场景描述
数据结构:一般这种下拉加载的,数据都是以卡片式的样式呈现,数据结构一般为[{}, {}, ...]
。然后下拉的新数据直接push到后面即可。
功能重点:这个功能的核心是怎么去监听到用户下拉到底了
响应式变量设计
响应式变量只需要这几个:
const [started, setStarted] = useState(false) // 是否已经开始加载(防抖,有延迟时间)
const [page, setPage] = useState(1) // 当前页码
const [list, setList] = useState([]) // 全部的列表数据,上划加载更多,累计
const [total, setTotal] = useState(0) // 后端返回的总数量
const haveMoreData = total > list.length // 是否还有数据可以加载
数据请求
咱们用到了ahooks里的useRequest
hook:
const { run: load, loading } = useRequest(
async () => {
const data = await getQuestionListService({ // 请求数据的api
page,
pageSize: 10,
keyword,
})
return data
},
{
manual: true,
onSuccess(result) {
const { list: l = [], total = 0 } = result
setList(list.concat(l)) // 累计推入
setTotal(total)
setPage(page + 1)
},
}
)
如果你要是不用ahooks就多声明一个loading响应式变量即可。
下拉加载更多(下拉区)的jsx
const LoadMoreContentElem = useMemo(() => {
if (!started || loading) return <Spin />
if (total === 0) return <Empty description="暂无数据" />
if (!haveMoreData) return <span>没有更多了...</span>
return <span>开始加载下一页...</span>
}, [started, loading, haveMoreData])
return (
<>
// ...
<div className={styles.footer}>
<div ref={containerRef}>{LoadMoreContentElem}</div>
</div>
</>
用getBoundingClientRect判断下拉区是否在屏幕显示器内
哎,这也不是什么新特性,好几年前就有了,可以用这个api去判断下拉区是否在屏幕显示器内,具体用法可以看https://blog.csdn.net/m0_52009348/article/details/119816872。
不得不说随着es标准的发布,很多功能的实现已经被新的api替代了,只是我们不知道,都用着“土方法”去实现,引人深思啊
有了这个api我们就可以实时监听下拉区是否在屏幕显示器内,当然还要加个防抖机制:
const containerRef = useRef<HTMLDivElement>(null)
const { run: tryLoadMore } = useDebounceFn(
() => {
const elem = containerRef.current
if (elem == null) return
const domRect = elem.getBoundingClientRect()
if (domRect == null) return
const { bottom } = domRect
// 出现在视图内
if (bottom <= document.body.clientHeight) {
load() // 真正加载数据
setStarted(true)
}
},
{
wait: 1000,
}
)
初始化且开启监听
万事俱备,只差触发
// 1. 当页面加载,或者搜索参数变化时,触发加载
useEffect(() => {
tryLoadMore() // 加载第一页,初始化
}, [searchParams])
// 2. 当页面滚动时,触发加载
useEffect(() => {
if (haveMoreData) {
window.addEventListener('scroll', tryLoadMore) // 防抖
}
return () => {
window.removeEventListener('scroll', tryLoadMore) // 解绑事件,重要!!!
}
}, [searchParams, haveMoreData])
页面滚动的useEffect监听searchParams, haveMoreData设计的很有意思,当重新搜索数据时,后端返回新数据了,一切从头开始计算,如果haveMoreData为false,说明搜索出来的数据不用分页没有更多了,不需要开启监听了。
尾巴
下次遇到了一样的需求直接cv大法拿走用吧~