在此之前:您可以移步去官网ahooks了解详情
useInfiniteScroll基本介绍
useInfiniteScroll 封装了常见的无限滚动逻辑。
const { data, loading, loadingMore, loadMore } = useInfiniteScroll(service);
useInfiniteScroll 的第一个参数 service
是一个异步函数,对这个函数的入参和出参有如下约定:
service
返回的数据必须包含list
数组,类型为{ list: any[], ...rest }
service
的入参为整合后的最新data
假如第一次请求返回数据为 { list: [1, 2, 3], nextId: 4 }
, 第二次返回的数据为 { list: [4, 5, 6], nextId: 7 }
, 则我们会自动合并 list
,整合后的的 data
为 { list: [1, 2, 3, 4, 5, 6], nextId: 7 }
。
注:这里官方说了对于返回的字段是list,才会帮你进行合并,所以可以跟后端的同学商量返回的数组数据用list做属性名,或者你也可以拿到后端的数据自己生成list属性名。
参数--(参照官网)
Result
参数 | 说明 | 类型 |
---|---|---|
data | service 返回的数据,其中的 list 属性为聚合后数据 | TData | undefined |
loading | 是否正在进行首次请求 | boolean |
loadingMore | 是否正在进行更多数据请求 | boolean |
noMore | 是否没有更多数据了,配置 options.isNoMore 后生效 | boolean |
loadMore | 加载更多数据,会自动捕获异常,通过 options.onError 处理 | () => void |
loadMoreAsync | 加载更多数据,与 loadMore 行为一致,但返回的是 Promise,需要自行处理异常 | () => Promise<TData> |
reload | 加载第一页数据,会自动捕获异常,通过 options.onError 处理 | () => void |
reloadAsync | 加载第一页数据,与 reload 行为一致,但返回的是 Promise,需要自行处理异常 | () => Promise<TData> |
mutate | 直接修改 data | (data?: TData) => void |
cancel | 忽略当前 Promise 的响应 | () => void |
options
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
target | 父级容器,如果存在,则在滚动到底部时,自动触发 loadMore 。需要配合 isNoMore 使用,以便知道什么时候到最后一页了。 | () => Element | Element | MutableRefObject<Element> | - |
isNoMore | 是否有最后一页的判断逻辑,入参为当前聚合后的 data | (data?: TData) => boolean | - |
threshold | 下拉自动加载,距离底部距离阈值 | number | 100 |
reloadDeps | 变化后,会自动触发 reload | any[] | - |
manual |
| boolean | false |
onBefore | service 执行前触发 | () => void | - |
onSuccess | service resolve 时触发 | (data: TData) => void | - |
onError | service reject 时触发 | (e: Error) => void | - |
onFinally | service 执行完成时触发 | (data?: TData, e?: Error) => void | - |
API使用
:这里只是介绍基本应用,如果是想了解详情请移步官网https://ahooks.gitee.io/zh-CN/hooks/use-infinite-scroll
const { data, loading, loadingMore, noMore } = useInfiniteScroll(callback,{
target: ref,
isNoMore: (d) => d?.total <= d?.list?.length,
});
data:返回的是处理后的数据(合并后);
loading:在请求时的状态,请求中为true,请求结束为false,注意,这里只会在初始化改变。
loadingMore:这个跟loading的不同之处在于每次在加载更多的时候的loading状态,每一次的改变都会及时改变true/false状态
noMore:是否还有更多的数据可加载,配合options中的isNoMore一起使用
target:就是要绑定的父容器
isNoMore:true为已经没有数据加载,false还有数据可加载
const transList = async (page: Page) => {
let result = await getRewards(page)
//这里的result数据是data的命名,所以转一下list --> list: result?.data?.data,
let data = {
list: result?.data?.data,
total: result?.data?.total,
...result,
}
return data
}
const { data, loading, loadingMore, noMore } = useInfiniteScroll(
(d) => {
// 无需写其他依赖,只需要在这里改变page就OK
const page = d ? Math.ceil(d?.data?.data.length / PAGE_SIZE) + 1 : 1
return transList({ page, pagesize: PAGE_SIZE })
},
{
// ref --> 直接useRef(),之后把这个use之后的ref绑定到父容器上
target: ref,
// d 就是transList函数返回的data
isNoMore: (d) => d?.total <= d?.list?.length,
},
)
1.这里传入的函数(transList)手动转成带list属性名的返回数据,hook监测到才会进行合并
2.d就是返回的合并后的数据
3.isNoMore: (d) => d?.total <= d?.list?.length,这里来做判断是否还要继续进行请求
详细代码
import React, { useState, useRef, useEffect, useMemo } from 'react'
import styles from './styles'
import { Box, Stack, Typography, CircularProgress } from '@mui/material'
import Image from 'next/image'
import { getRewards } from '@/api/profile'
import { useInfiniteScroll } from 'ahooks'
import moment from 'moment'
import NoData from '../NoData'
type Page = {
page: number
pagesize: number
}
const Reward: React.FC = () => {
const PAGE_SIZE = 3
const ref = useRef<HTMLDivElement>(null)
const transList = async (page: Page) => {
let result = await getRewards(page)
let params = {
list: result?.data?.data,
total: result?.data?.total,
...result,
}
return params
}
const { data, loading, loadingMore, noMore } = useInfiniteScroll(
(d) => {
const page = d ? Math.ceil(d?.data?.data.length / PAGE_SIZE) + 1 : 1
return transList({ page, pagesize: PAGE_SIZE })
},
{
target: ref,
isNoMore: (d) => d?.total <= d?.list?.length,
},
)
// data返回的数据,会是合并后
const rewardList = data?.list || []
return (
//渲染data中的数据
<Box sx={[rewardList?.length && styles.rewardBox]} ref={ref}>
{rewardList &&
!!rewardList?.length &&
rewardList.map((reword: any, index: number) => (
<Stack key={index} direction="row" spacing={1} sx={{ marginBottom: 60 }}>
......
</Stack>
))}
// loading,loadingMore会在请求的时候发生改变,可以做loading效果,noMore在没有数据时会为true,其他情况多都是false,可以用来做nodata效果
<Box sx={{ height: 200 }}>
{loading || loadingMore ? <CircularProgress color="secondary" /> : noMore && <NoData />}
</Box>
</Box>
)
}
export default Reward
以上就是滚动加载最基本的使用。