InfiniteTable

InfiniteTable — preset для длинных списков без пагинации: включает infiniteLoading, подключает scrollRef и IntersectionObserver через onLoadMore / hasMore, по умолчанию включает виртуализацию строк (enableRowVirtualization).

Когда использовать

  • Лента записей с подгрузкой при скролле.
  • Не нужна постраничная навигация.

Когда не нужен:

  • Классическая пагинация — SimpleTable / ServerTable.
  • Нужен полный контроль над виртуализацией колонок или кастомными параметрами virtualizer — базовый Table.

Чтобы отключить виртуализацию строк: enableRowVirtualization={false}.

Примеры использования

Подгрузка при скролле

Подгрузка при скролле
tsx
import { InfiniteTable, SimpleColumnDef } from '@cloud-ru/ds-table';
import { useCallback, useState } from 'react';

type User = {
  id: string;
  name: string;
  email: string;
  role: string;
};

const ALL_USERS: User[] = Array.from({ length: 20 }, (_, index) => ({
  id: `u-${index + 1}`,
  name: `Пользователь ${index + 1}`,
  email: `user${index + 1}@example.com`,
  role: index % 2 === 0 ? 'Editor' : 'Viewer',
}));

const columns: SimpleColumnDef<User>[] = [
  { key: 'name', header: 'Имя', width: 200 },
  { key: 'email', header: 'Email', width: 240 },
  { key: 'role', header: 'Роль', width: 140 },
];

const PAGE = 5;

export function InfiniteTableBasic() {
  const [items, setItems] = useState(() => ALL_USERS.slice(0, PAGE));
  const [loading, setLoading] = useState(false);
  const hasMore = items.length < ALL_USERS.length;

  const onLoadMore = useCallback(() => {
    setLoading(true);
    window.setTimeout(() => {
      setItems(ALL_USERS.slice(0, Math.min(items.length + PAGE, ALL_USERS.length)));
      setLoading(false);
    }, 300);
  }, [items.length]);

  return (
    <div style={{ display: 'grid', gridTemplateRows: 'minmax(0, 1fr)', height: 360 }}>
      <InfiniteTable
        data={items}
        columns={columns}
        getRowId={user => user.id}
        loading={loading}
        hasMore={hasMore}
        onLoadMore={onLoadMore}
        outline
      />
    </div>
  );
}

Через хук useInfiniteTableProps

Через хук useInfiniteTableProps
tsx
import { SimpleColumnDef, Table, useInfiniteTableProps } from '@cloud-ru/ds-table';
import { useCallback, useState } from 'react';

type User = { id: string; name: string; email: string };

const ALL_USERS: User[] = Array.from({ length: 12 }, (_, index) => ({
  id: `u-${index + 1}`,
  name: `Пользователь ${index + 1}`,
  email: `user${index + 1}@example.com`,
}));

const columns: SimpleColumnDef<User>[] = [
  { key: 'name', header: 'Имя', width: 200 },
  { key: 'email', header: 'Email', width: 240 },
];

const PAGE = 4;

export function InfiniteTableWithHook() {
  const [items, setItems] = useState(() => ALL_USERS.slice(0, PAGE));
  const [loading, setLoading] = useState(false);
  const hasMore = items.length < ALL_USERS.length;

  const onLoadMore = useCallback(() => {
    setLoading(true);
    window.setTimeout(() => {
      setItems(ALL_USERS.slice(0, Math.min(items.length + PAGE, ALL_USERS.length)));
      setLoading(false);
    }, 300);
  }, [items.length]);

  const tableProps = useInfiniteTableProps({
    data: items,
    columns,
    getRowId: user => user.id,
    loading,
    hasMore,
    onLoadMore,
  });

  return (
    <div style={{ display: 'grid', gridTemplateRows: 'minmax(0, 1fr)', height: 360 }}>
      <Table {...tableProps} outline />
    </div>
  );
}

Без подгрузки (hasMore: false)

Без подгрузки (hasMore: false)
tsx
import { InfiniteTable, SimpleColumnDef } from '@cloud-ru/ds-table';

type User = { id: string; name: string; email: string };

const USERS: User[] = [
  { id: 'u-1', name: 'Анна Иванова', email: 'anna@example.com' },
  { id: 'u-2', name: 'Борис Петров', email: 'boris@example.com' },
];

const columns: SimpleColumnDef<User>[] = [
  { key: 'name', header: 'Имя', width: 200 },
  { key: 'email', header: 'Email', width: 240 },
];

export function InfiniteTableStatic() {
  return (
    <div style={{ display: 'grid', gridTemplateRows: 'minmax(0, 1fr)', height: 280 }}>
      <InfiniteTable data={USERS} columns={columns} getRowId={user => user.id} hasMore={false} outline />
    </div>
  );
}

Storybook