useVirtualList

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

一个通过只渲染用户可见项目来帮助更高效地渲染大型列表的 React Hook,即虚拟列表,支持动态高度,横向、纵向滚动等场景。

场景

  • 长列表性能优化: 通过只渲染可视区域内的元素,优化长列表的滚动性能
  • 动态内容尺寸处理: 支持基于内容尺寸动态计算并渲染可视区内的项目

演示

源码

Fixed Size Virtual List (Vertical, 100,000 Count)

Dynamic Size Virtual List (Horizontal, 100,000 Count)

用法

const containerRef = useRef(null)
const wrapperRef = useRef(null)

const [list, actions] = useVirtualList(largeList, {
  // 或者直接使用 `querySelector` 的字符串: `#scroll-container`
  containerTarget: containerRef,
  // 或者直接使用 `querySelector` 的字符串: `#scroll-wrapper`
  wrapperTarget: wrapperRef,
  // 确保每个项目的高度为 48(包括 margin、padding 等),或者一个返回每个项目高度的函数
  itemHeight: 48,
})

// actions.scrollTo(1000)
// actions.scrollToStart()
// actions.scrollToEnd()

// 使用 `list[0].data` 和 `list[0].index` 来渲染项目

return (
  <div ref={containerRef} style={{ height: 300, overflow: 'auto' }}>
    <div ref={wrapperRef}>
      {list.map((item) => (
        <div key={item.index} style={{ height: 48 }}>
          {item.data}
        </div>
      ))}
    </div>
  </div>
)

源码

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

API

const [list, actions] = useVirtualList(largeList, options)

大数据列表 LargeList

一个包含大量数据的任意合法数组。

选项 Options

interface UseVirtualListBaseOptions {
  /**
   * 容器元素,ElementTarget 类型,通常是可滚动的元素。
   */
  containerTarget: NonNullable<ElementTarget<HTMLElement>>
  /**
   * Wrapper 元素,ElementTarget 类型,通常是包含所有项目的元素。
   *
   * marginTop 和 height(横向滚动时为 marginLeft 和 width)将被设置为所有项目的总高度(宽度)。
   */
  wrapperTarget: NonNullable<ElementTarget<HTMLElement>>
  /**
   * 在视野外(竖直滚动时为上下,横向为左右)分别额外渲染的项目数量。默认为 5。
   * 
   * @defaultValue 5
   */
  overscan?: number
}

interface UseVerticalVirtualListOptions<D> extends UseVirtualListBaseOptions {
  /**
   * 每个项目的高度,或者返回每个项目高度的函数,接受参数为 index 和 item。
   *
   * 当设置了 `itemHeight` 时,列表为纵向渲染模式,优先级高于 `itemWidth`。
   */
  itemHeight: number | ((index: number, item: D) => number)
}

interface UseHorizontalVirtualListOptions<D> extends UseVirtualListBaseOptions {
  /**
   * 每个项目的宽度,或者返回每个项目宽度的函数,接受参数为 index 和 item。
   *
   * 当设置了 `itemWidth` 且未设置 `itemHeight` 时,列表为横向渲染模式。
   */
  itemWidth: number | ((index: number, item: D) => number)
}

export type UseVirtualListOptions<D> = UseVerticalVirtualListOptions<D> | UseHorizontalVirtualListOptions<D>

返回值

export interface UseVirtualListReturnsActions {
  /**
   * 滚动到指定索引的项目。
   *
   * @param {Number} index 想要滚动到的项目索引。
   */
  scrollTo: (index: number) => void
  /**
   * 滚动到列表的开始,根据选项自动使用纵向或横向滚动。
   */
  scrollToStart: () => void
  /**
   * 滚动到列表的结束,根据选项自动使用纵向或横向滚动。
   */
  scrollToEnd: () => void
}

export interface UseVirtualListReturnsListItem<D> {
  /**
   * 项目的原始数据项。
   */
  data: D
  /**
   * 项目的原始数组索引。
   */
  index: number
}

export type UseVirtualListReturns<D> = readonly [
  /**
   * 虚拟列表的项目数组,即实际被渲染的项目列表,包含可视区域和额外渲染区域。
   */
  list: UseVirtualListReturnsListItem<D>[],
  /**
   * 操作虚拟列表的方法集合。
   */
  UseVirtualListReturnsActions,
]