← Назад к вопросам

Как реализуешь таблицы в проекте?

2.3 Middle🔥 131 комментариев
#Soft Skills и рабочие процессы

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Реализация таблиц на фронтенде

1. Семантический HTML для базовых таблиц

Начинаю с правильной HTML-структуры:

<table>
  <thead>
    <tr>
      <th scope="col">Имя</th>
      <th scope="col">Email</th>
      <th scope="col">Статус</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Иван</td>
      <td>ivan@example.com</td>
      <td><span class="badge badge-success">Active</span></td>
    </tr>
  </tbody>
</table>

2. React компонент с типизацией

Структурирую компонент таблицы с правильной архитектурой:

interface TableColumn<T> {
  key: keyof T;
  label: string;
  render?: (value: T[keyof T], row: T) => ReactNode;
  width?: string;
  sortable?: boolean;
}

interface TableProps<T> {
  data: T[];
  columns: TableColumn<T>[];
  isLoading?: boolean;
  onRowClick?: (row: T) => void;
}

export function Table<T extends { id: string | number }>({
  data,
  columns,
  isLoading,
  onRowClick
}: TableProps<T>) {
  return (
    <div className="overflow-x-auto">
      <table className="w-full border-collapse">
        <thead className="bg-surface-secondary">
          <tr>
            {columns.map((col) => (
              <th 
                key={String(col.key)} 
                className="text-left px-4 py-3 font-semibold"
                style={{ width: col.width }}
              >
                {col.label}
              </th>
            ))}
          </tr>
        </thead>
        <tbody>
          {isLoading ? (
            <tr><td colSpan={columns.length} className="text-center py-8">Загрузка...</td></tr>
          ) : data.length === 0 ? (
            <tr><td colSpan={columns.length} className="text-center py-8">Нет данных</td></tr>
          ) : (
            data.map((row) => (
              <tr 
                key={row.id}
                className="border-b hover:bg-surface-hover cursor-pointer"
                onClick={() => onRowClick?.(row)}
              >
                {columns.map((col) => (
                  <td key={String(col.key)} className="px-4 py-3">
                    {col.render ? col.render(row[col.key], row) : String(row[col.key])}
                  </td>
                ))}
              </tr>
            ))
          )}
        </tbody>
      </table>
    </div>
  );
}

3. Пагинация и сортировка

Добавляю управление данными через хук:

interface UseTableOptions {
  pageSize: number;
}

export function useTable<T>(data: T[], options: UseTableOptions) {
  const [page, setPage] = useState(0);
  const [sortBy, setSortBy] = useState<keyof T | null>(null);
  const [sortOrder, setSortOrder] = useState<'asc' | 'desc'>('asc');

  const sortedData = useMemo(() => {
    if (!sortBy) return data;
    
    return [...data].sort((a, b) => {
      const aVal = a[sortBy];
      const bVal = b[sortBy];
      
      if (aVal < bVal) return sortOrder === 'asc' ? -1 : 1;
      if (aVal > bVal) return sortOrder === 'asc' ? 1 : -1;
      return 0;
    });
  }, [data, sortBy, sortOrder]);

  const paginatedData = useMemo(() => {
    const start = page * options.pageSize;
    return sortedData.slice(start, start + options.pageSize);
  }, [sortedData, page, options.pageSize]);

  const totalPages = Math.ceil(sortedData.length / options.pageSize);

  return {
    data: paginatedData,
    page,
    setPage,
    totalPages,
    sortBy,
    setSortBy,
    sortOrder,
    setSortOrder,
    total: sortedData.length
  };
}

4. Использование в компоненте

interface User {
  id: string;
  name: string;
  email: string;
  status: 'active' | 'inactive';
  joinedDate: string;
}

export function UsersTable() {
  const [users, setUsers] = useState<User[]>([]);
  const [loading, setLoading] = useState(true);
  
  const table = useTable(users, { pageSize: 10 });

  const columns: TableColumn<User>[] = [
    {
      key: 'name',
      label: 'Имя',
      sortable: true,
      render: (name) => <span className="font-medium">{name}</span>
    },
    {
      key: 'email',
      label: 'Email',
      sortable: true
    },
    {
      key: 'status',
      label: 'Статус',
      render: (status) => (
        <span className={status === 'active' ? 'bg-green-100' : 'bg-gray-100'}>
          {status}
        </span>
      )
    }
  ];

  return (
    <div className="space-y-4">
      <Table 
        data={table.data}
        columns={columns}
        isLoading={loading}
      />
    </div>
  );
}

5. Фиксированные заголовки (sticky header)

Для больших таблиц делаю заголовок липким:

<div className="relative h-96 overflow-y-auto">
  <table className="w-full">
    <thead className="sticky top-0 bg-surface-secondary z-10">
      {/* thead содержимое */}
    </thead>
    <tbody>
      {/* tbody содержимое */}
    </tbody>
  </table>
</div>

6. Фильтрация и поиск

export function TableWithFilters() {
  const [searchText, setSearchText] = useState('');

  const filteredData = useMemo(() => {
    return data.filter(row => 
      !searchText || row.name.toLowerCase().includes(searchText.toLowerCase())
    );
  }, [data, searchText]);

  return (
    <div className="space-y-4">
      <input
        type="text"
        value={searchText}
        onChange={(e) => setSearchText(e.target.value)}
        placeholder="Поиск..."
      />
      
      <Table data={filteredData} columns={columns} />
    </div>
  );
}

7. Responsive таблица для мобилки

Для мобильных устройств преобразую таблицу в карточки:

export function ResponsiveTable() {
  const isMobile = window.innerWidth < 768;

  return isMobile ? (
    <div className="space-y-4">
      {data.map(row => (
        <div key={row.id} className="border p-4 rounded">
          {columns.map(col => (
            <div key={String(col.key)} className="flex justify-between mb-2">
              <span className="font-semibold">{col.label}</span>
              <span>{row[col.key]}</span>
            </div>
          ))}
        </div>
      ))}
    </div>
  ) : (
    <Table data={data} columns={columns} />
  );
}

8. Best Practices для таблиц

  • Используй семантический HTML (table, thead, tbody)
  • Типизируй данные через TypeScript generics
  • Реализуй пагинацию для больших наборов
  • Добавь сортировку и фильтрацию
  • Обеспечь доступность (a11y)
  • Сделай таблицу responsive для мобилки
  • Используй виртуализацию для 1000+ строк

Таблицы — один из важных элементов UI. Правильная реализация требует внимания к доступности, производительности и UX.

Как реализуешь таблицы в проекте? | PrepBro