JGuang

无限滚动

InfiniteScroll · Ant Design Mobile

交互预览
npm install antd-mobile@5.43.0

无限滚动 InfiniteScroll

列表滚动到底部自动加载更多数据。

安装

npm install antd-mobile@5.43.0

antd-mobile 是移动网页组件库,不是 React Native 或小程序组件。React 19 项目需在入口用 unstableSetRender 注册 createRoot,否则 Toast、Dialog 等命令式 API 无法渲染;深色模式通过 <html data-prefers-color-scheme="dark"> 切换。

示例来自 Ant Design Mobile 官方仓库(MIT),固定在 npm 5.43.0 对应的提交;示例中的 DemoBlock 等展示辅助组件由 JGuang 重写,不属于 antd-mobile 的 API。

列表滚动到底部自动加载更多数据。

何时使用

用户想看到新的数据时,可以上滑页面自动加载数据。

当 hasMore 属性为 true 时,用户页面滚动到底部 threshold (默认为 250px)时无限滚动组件会调用定义的 loadMore 函数。

示例

InfiniteScroll

属性

属性说明类型默认值
children渲染自定义指引内容React.ReactNode | ((hasMore: boolean, failed: boolean, retry: () => void) => React.ReactNode)默认的指引提示
hasMore是否还有更多内容boolean-
loadMore加载更多的回调函数(isRetry: boolean) => Promise<void>-
threshold触发加载事件的滚动触底距离阈值,单位为像素number250

InfiniteScroll 会自动对 loadMore 函数加锁,避免重复的请求,但是前提是 loadMore 函数需要返回一个正确的 Promise,下面是正确和错误的用法示例:

function loadMore() { // 错误
  doRequest()
}

async function loadMore() { // 错误
  doRequest()
}

async function loadMore() { // 正确
  await doRequest()
}

function loadMore() { // 正确
  return doRequest()
}

自定义 Content

如果需要的话,InfiniteScroll 允许自定义展示内容,这个内容可以包含任何元素,包括 svg 和带有 css 动画的元素。

配合搜索使用

滚动加载无限长列表

结合 react-virtualized 实现滚动加载无限长列表

请求失败时支持点击重试

常见问题

是否支持下拉刷新?

下拉刷新是另一个组件:PullToRefresh。

为什么配合 ahooks 的 useDebounceFn 或者 useThrottleFn 使用时会出现报错?

InfiniteScroll 本身已经包含了防止并发的重复请求的逻辑,所以请不要配合 useDebounceFn 或 useThrottleFn 使用,它们之间会出现逻辑冲突。

为什么 InfiniteScroll 由隐藏切换为显示时,一直处于 loading?

在使用 InfiniteScroll 组件时,其内部会检测组件是否可见。可见时会调用 loadMore 来加载数据;不可见时,不会调用 loadMore(这是必要的,可以避免不必要的数据请求)。并且,该检查机制,在组件初次渲染以及每次重新渲染时都会进行,以确保正确加载数据。

但有些场景下(比如,配合 Tabs 组件使用时),可能会遇到 InfiniteScroll 组件一直 loading 且未加载数据的情况,比如以下示例:

<Tabs>
  <Tabs.Tab title='水果' key='fruits'>菠萝</Tabs.Tab>
  <Tabs.Tab title='蔬菜' key='vegetables' forceRender>
    <InfiniteScroll
      hasMore={true}
      loadMore={() => {
        // 切换到该 Tab 时,该函数不执行
      }}
    />
  </Tabs.Tab>
</Tabs>

问题描述:Tabs 组件默认展示第一个 Tab 项的内容,所以,第二个 Tab 项的内容 InfiniteScroll 是不可见的。但第二个 Tab 添加了 forceRender 属性,所以即使不可见,其内容也会渲染。本次渲染 InfiniteScroll组件时,由于该组件不可见,所以,不会调用 loadMore 函数,这是正常的,跟我们的预期相同。

但是,当我们切换到第二个 Tab 展示 InfiniteScroll 组件时,发现 InfiniteScroll 组件并没有调用 loadMore 函数,这一点跟我们预期不同,我们希望此时 loadMore 函数被调用。

原因说明:点击切换 Tabs 组件的 Tab 项时,会修改 Tabs 组件的高亮状态,此时,Tabs 组件会重新渲染。但是,要注意的是只有在 Tabs 组件自身的内容才会被重新渲染,而 InfiniteScroll 组件是在 Tabs 组件外部的,并非 Tabs 组件自身内容。所以,切换 Tab 时,InfiniteScroll 组件并不会重新渲染,也就没有再次触发它的检查机制。

解决方式:

  • 方式一:去掉 InfiniteScroll 组件所在 Tab 的 forceRender(只在激活并展示 Tab 时,才渲染 InfiniteScroll 组件,此时,该组件可见就会正确调用 loadMore 来加载数据)
  • 方式二:通过受控组件模式使用 Tabs 组件(切换 Tab 时,更新了 InfiniteScroll 组件所在组件的状态,此时,会重新渲染 InfiniteScroll 组件,也就会再次触发检查机制,而该组件可见,就会正确调用 loadMore 来加载数据),示例如下:
const [activeKey, setActiveKey] = useState('fruits')

<Tabs activeKey={activeKey} onChange={setActiveKey}>
  ...
</Tabs>