useInfiniteList

这个 Hook 自 v1.7.0 版本起可用。

一个用来处理无限列表的 React Hook,支持无限滚动、点击加载更多等场景,内置了数据请求、探底检测、表单管理、分页、多选、加载状态等功能。

useInfiniteList 是以下几个 Hooks 的上层封装:

  • useInfiniteScroll 提供探底检测、数据是否加载完毕等功能
  • useForm 提供表单管理功能,用于搜索、筛选等
  • useAsyncFn 提供数据请求、生命周期等功能
  • usePagination 提供分页状态,管理分页查询参数
  • useMultiSelect 提供多选状态,常用于多选进行批量操作

场景

useInfiniteList = 无限滚动/点击加载 + 自动管理分页 + 表单查询(可选) + 多选操作(可选)

演示

源码

1. Scroll to Load More

Name:
Gender:
Color:
Current Page:
0
Next Request Page:
1
PageSize:
10
Data Count:
0
Loading:
false
Load Done:
false
No selected

2. Click to Load More

用法

interface Item { id: number; name: string; }
interface Data { data: Item[]; total: number; }
interface FormState { name: string; gender: string; color: string[]; }

const ref = useRef<HTMLDivElement>(null) // 用于探底检测的目标元素,仅滚动加载需要

const {
  list, // fetcher 返回的数据组成返回值列表,Data[]
  fullList, // fetcher 返回的数据里的列表展开后组成的项目列表,Item[]
  form, // 表单状态和操作
  selection, // 多选状态和操作
  paginationState, // 分页状态,请求的页码信息
  loading, // 加载状态
  isLoadDone // 是否加载完毕
} = useInfiniteList<Data, Item, FormState>({
  target: ref, // 用于探底检测的目标元素,仅滚动加载需要
  fetcher: fetchPagination, // 请求数据的函数
  mapFullList: (d) => d.data, // 映射数据列表的 map 函数
  canLoadMore: (previousData, dataList, fullList) => {
    if (!previousData) return true // 无上次请求结果,则为首次加载
    // 当前列表长度小于总数时,可以继续加载,否则不再加载
    return fullList.length < previousData.total 
  },
  form: { // 配置 useForm
    initialValue: {
      name: '',
      gender: 'Boy',
      color: ['Red'],
    },
  },
  pagination: { pageSize: 10 }, // 配置 usePagination
  immediateQueryKeys: ['color', 'gender'], // 立即查询的表单字段
})

return (
  <div>
    <MyForm form={form} />
    <div ref={ref} className="h-360px overflow-scroll">
      <MyInfiniteScrollList list={fullList} />
    </div>
  </div>
)

源码

点击下方链接跳转 GitHub 查看源代码。

API

const {
  list, fullList, form, selection,
  paginationState, loading, isLoadDone
} = useInfiniteList<Data, Item, FormState>(options)

选项 Options

export interface UseInfiniteListOptions<
  Data,
  Item,
  FormState extends object,
  Fetcher extends AnyFunc,
  Container extends HTMLElement,
> {
  /**
   * 容器元素,用于探底检测
   */
  target?: ElementTarget<Container>
  /**
   * 数据获取函数,可返回类似 { data: Item[], total: number } 的数据
   */
  fetcher?: Fetcher
  /**
   * 数据映射函数,用于获取每一次返回里的数据列表
   */
  mapFullList?: (data: Data) => Item[]
  /**
   * 表单选项,用于配置 `useForm`,参考 `useForm` 了解更多
   */
  form?: UseFormOptions<FormState>
  /**
   * 异步函数选项,用于配置 `useAsyncFn`,参考 `useAsyncFn` 了解更多
   */
  asyncFn?: Omit<UseAsyncFnOptions<Fetcher, Data, any>, 'initialParams'>
  /**
   * 分页数据选项,用于配置 `usePagination`,参考 `usePagination` 了解更多
   */
  pagination?: UsePaginationOptions<Data>
  /**
   * 是否可以继续加载更多的判断函数
   */
  canLoadMore?: (previousData: Data | undefined, dataList: Data[], fullList: Item[]) => boolean
  /**
   * 滚动加载选项,用于配置 `useInfiniteScroll`,参考 `useInfiniteScroll` 了解更多
   */
  infiniteScroll?: Omit<UseInfiniteScrollOptions<Data>, 'canLoadMore'>
  /**
   * 立即查询的表单字段,当表单字段变化时立即重置数据并开启新的一轮查询
   */
  immediateQueryKeys?: (keyof FormState)[]
}

返回值

返回值包含 useInfiniteScroll 的返回值,参考 useInfiniteScroll 了解更多。

export interface UseInfiniteListReturns<Data, Item, FormState extends object>
  extends Omit<UseInfiniteScrollReturns, 'reset'> {
  /**
   * 重置所有状态,并重新查询
   */
  reset: () => void
  /**
   * 表单状态和操作
   */
  form: UseFormReturns<FormState>
  /**
   * 加载状态
   */
  list: Data[]
  /**
   * 是否加载完毕
   */
  fullList: Item[]
  /**
   * 多选状态和操作
   */
  selection: UseMultiSelectReturnsState<Item> & UseMultiSelectReturnsActions<Item>
  /**
   * 分页状态
   */
  paginationState: UsePaginationReturnsState<Data>
}