useInfiniteScroll基本介绍
useInfiniteScroll 封装了常见的无限滚动逻辑。
const { data, loading, loadingMore, loadMore } = useInfiniteScroll(service);
useInfiniteScroll 的第一个参数 service 是一个异步函数,对这个函数的入参和出参有如下约定:
- service 返回的数据必须包含 list 数组,类型为 { list: any[], …rest }
- service 的入参为整合后的最新 data
假如第一次请求返回数据为 { list: [1, 2, 3] }, 第二次返回的数据为 { list: [4, 5, 6] }, 则我们会自动合并 list,整合后的的 data 为 { list: [1, 2, 3, 4, 5, 6] }。
注:这里官方说了对于返回的字段是list,才会帮你进行合并,所以可以跟后端的同学商量返回的数组数据用list做属性名,或者你也可以拿到后端的数据自己生成list属性名。
其他参数及API请访问官网:https://ahooks.js.org/zh-CN/hooks/use-infinite-scroll
实际使用
import './styles.less';
import { useEffect, useRef, useState } from 'react';
import { useInfiniteScroll } from 'ahooks';
import { Spin } from 'antd';
const GlobalHeader = (props: any) => {
const noticeScrollParentRef = useRef(null) as any;
// 数据请求
const loadNoticeList = async (current = 1) => {
const result = await apiGetNoticeList({
pageSize: 10,
current,
});
// 必须包含 list 这个字段,最好 total、page 也带上 { list: any[], ... }
const dataList = {
list: result?.data?.records || [],
total: result?.data?.total || 0,
page: current,
};
return dataList;
};
// 滚动加载
const {
data: noticeData,
loading: noticeLoading,
loadingMore: noticeLoadingMore,
noMore: noticeNoMore,
reload: noticeReload,
} = useInfiniteScroll(
(d) => {
const page = d ? Math.ceil(d?.list?.length / PAGE_SIZE) + 1 : 1;
return loadNoticeList(page);
},
{
target: noticeScrollParentRef,
isNoMore: (d: any) => d?.total <= d?.list?.length,
threshold: 10,
// 以下onBefore和onFinally,如出现滚动条跳顶情况才需要
onBefore: () => {
// 优化加载更多时,滚动条会自动跳顶的问题
setTimeout(() => {
const parentRef = noticeScrollParentRef.current;
if (parentRef && parentRef.childElementCount > 10) {
const itemHeight = parentRef.firstChild?.offsetHeight || 80;
parentRef.scrollTo({
top: parentRef.lastChild?.offsetTop - itemHeight,
behavior: 'instant',
});
}
});
},
onFinally: (data) => onFinally(data, noticeScrollParentRef),
},
);
// 优化加载更多时,滚动条会自动跳顶的问题
const onFinally = (data: any, ref: any) => {
if (data?.page === 1) return;
const parentRef = ref.current;
if (!parentRef) return;
const itemHeight = parentRef.firstChild?.offsetHeight || 80;
parentRef.scrollTo({
top:
parentRef.lastChild?.offsetTop - (data?.list?.length + 1) * itemHeight,
behavior: 'instant',
});
};
return (
<div ref={noticeScrollParentRef} className="scroll-wrapper">
{noticeData && noticeData?.list && noticeData?.list.length > 0
? noticeData?.list.map((item: any) => (
<div key={item.id}>xxx</div>
))
: null}
{noticeLoading || noticeLoadingMore ? (
<Spin spinning>
<p className="footer-loading"></p>
</Spin>
) : null}
</div>
)
};
样式(外层必须有高度和开启滚动):
// styles.less
.scroll-wrapper {
width: 100%;
height: 200px;
overflow: auto;
}