useVirtualList

React hook that allows you to use virtual list to render huge chunks of list data.

Attention: You must set a height for the container, otherwise it is possible that the entire list data will be rendered.

Usage

 show code
import { useMemo, useState } from "react";
import { useVirtualList } from "@reactuses/core";

const allItems = Array.from(Array(9999).keys()).map(i => ({
  height: i % 2 === 0 ? 42 : 84,
  size: i % 2 === 0 ? "small" : "large",
}));

const Demo = () => {
  const [index, setIndex] = useState(0);
  const [search, setSearch] = useState("");

  const filteredItems = useMemo(() => {
    return allItems.filter(i => i.size.startsWith(search.toLowerCase()));
  }, [search]);

  const { list, containerProps, wrapperProps, scrollTo } = useVirtualList(
    filteredItems,
    {
      itemHeight: i => filteredItems[i].height + 8,
      overscan: 10,
    }
  );

  const handleScrollTo = () => {
    scrollTo(index);
  };

  return (
    <div>
      <div>
        <div> Jump to index</div>
        <input
          value={index}
          onChange={v => setIndex(v.currentTarget.valueAsNumber)}
          placeholder="Index"
          type="number"
        />
        <button onClick={handleScrollTo} style={{ marginLeft: 20 }}>
          Go
        </button>
      </div>
      <div style={{ marginTop: 20 }}>
        <div>Filter list by size</div>
        <input
          value={search}
          placeholder="e.g. small, medium, large"
          onChange={v => setSearch(v.currentTarget.value)}
          type="search"
          style={{ minWidth: "20rem" }}
        />
      </div>
      <div
        {...containerProps}
        style={{
          padding: "0.5rem",
          marginTop: 20,
          ...containerProps.style,
        }}
      >
        <div style={wrapperProps.style}>
          {list.map((item) => {
            const { index, data } = item;
            return (
              <div
                key={index}
                style={{
                  height: `${data.height}px`,
                  display: "flex",
                  justifyContent: "center",
                  alignItems: "center",
                  borderWidth: 1,
                  borderStyle: "solid",
                  marginBottom: "0.5rem",
                }}
              >
                Row {index} <span>({data.size})</span>
              </div>
            );
          })}
        </div>
      </div>
    </div>
  );
};

Example

Jump to index
Filter list by size

Type Declarations

export interface UseVirtualListOptions {
  /**
   * container default height
   *
   * @default 300
   */
  containerHeight?: number
  /**
   * item height, accept a pixel value or a function that returns the height
   */
  itemHeight: number | ((index: number) => number)
  /**
   * the extra buffer items outside of the view area
   *
   * @default 5
   */
  overscan?: number
}
export interface UseVirtualListItem<T> {
  data: T
  index: number
}
export interface UseVirtualListReturn<T> {
  list: UseVirtualListItem<T>[]
  scrollTo: (index: number) => void
  containerProps: {
    ref: RefObject<any>
    onScroll: () => void
    style: Partial<CSSProperties>
  }
  wrapperProps: {
    style: {
      width: string
      height: string
      marginTop: string
    }
  }
}
export default function useVirtualList<T = any>(
  list: T[] | undefined,
  options: UseVirtualListOptions
): UseVirtualListReturn<T>