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

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

2.0 Middle🔥 161 комментариев
#React

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

🐱
deepseek-v3.2PrepBro AI4 апр. 2026 г.(ред.)

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

Антипаттерны при создании таблицы с подкомпонентами на React/Vue

При разработке сложных таблиц с раскрывающимися подкомпонентами (например, детализация строк, вложенные данные) важно избегать следующих антипаттернов:

1. Мутация состояния напрямую и отсутствие ключей

// АНТИПАТТЕРН: Прямая мутация и отсутствие key
const TableRow = ({ item }) => {
  const [expanded, setExpanded] = useState(false);
  
  const handleClick = () => {
    expanded = !expanded; // Прямая мутация!
  };
  
  return (
    <div> {/* Нет key! */}
      <tr onClick={handleClick}>
        <td>{item.name}</td>
      </tr>
      {expanded && <SubComponent data={item.details} />}
    </div>
  );
};

Проблемы:

  • Прямая мутация состояния не вызывает ререндер
  • Отсутствие key приводит к некорректному сопоставлению элементов, особенно при раскрытии/закрытии строк
  • Может вызвать "скачки" раскрытых состояний при обновлении данных

2. Избыточные ререндеры из-за inline-функций и объектов

// АНТИПАТТЕРН: Создание новых функций/объектов на каждом рендере
const Table = ({ data }) => {
  return (
    <table>
      {data.map(item => (
        <TableRow
          key={item.id}
          onExpand={() => { /* Новая функция каждый раз */ }}
          config={{ expanded: false }} // Новый объект каждый раз
          subComponent={<SubComponent data={item.details} />} // Новый JSX
        />
      ))}
    </table>
  );
};

Последствия:

  • Даже при изменении одной строки перерендеривается вся таблица
  • Memoization (React.memo, useMemo, useCallback) не работает эффективно
  • Падение производительности при больших наборах данных

3. Глубокое вложение и нарушение принципа единственной ответственности

// АНТИПАТТЕРН: Монолитный компонент со всей логикой
const MonsterTableRow = ({ item, onUpdate, onDelete, onExpand, ...rest }) => {
  // 200+ строк кода, включая:
  // - Логику раскрытия
  // - Валидацию данных
  // - Форматирование
  // - Сетевые запросы
  // - Анимации
  // - Обработку 10+ событий
};

Проблемы:

  • Невозможно переиспользовать части функциональности
  • Сложность тестирования
  • Связывание логики представления с бизнес-логикой

4. Хранение состояния раскрытия в глобальном сторе без необходимости

// АНТИПАТТЕРН: Использование Redux для UI-состояния
const TableRow = ({ item, expandedItems, dispatch }) => {
  // Для каждой строки обращение к глобальному состоянию
  const isExpanded = expandedItems.includes(item.id);
  
  const toggleExpand = () => {
    dispatch(toggleItemExpansion(item.id)); // Дорогое обновление
  };
  
  return (/* ... */);
};

Недостатки:

  • Избыточные подписки на глобальное состояние
  • Сложная логика селекторов для производных данных
  • Ненужная централизация UI-состояния

5. Отсутствие виртуализации для больших таблиц

// АНТИПАТТЕРН: Рендер всех строк независимо от видимости
const HugeTable = ({ data }) => {
  return (
    <div style={{ height: '500px', overflow: 'auto' }}>
      {data.map(item => ( // 10,000 элементов!
        <ExpensiveRow 
          key={item.id}
          item={item}
          // Все строки монтируются, даже невидимые
        />
      ))}
    </div>
  );
};

Результат:

  • Блокировка основного потока на сотни миллисекунд
  • Чрезмерное использование памяти
  • Медленная прокрутка и отзывчивость интерфейса

6. Неконтролируемое состояние с побочными эффектами

// АНТИПАТТЕРН: Побочные эффекты в обработчиках раскрытия
const TableRow = ({ item }) => {
  const [expanded, setExpanded] = useState(false);
  
  const handleExpand = () => {
    setExpanded(true);
    fetchDetails(item.id); // Побочный эффект
    trackAnalytics('row_expanded'); // Еще один эффект
    if (someCondition) {
      updateOtherComponent(); // Связанная логика
    }
  };
};

Проблемы:

  • Сложность отслеживания потока данных
  • Нарушение принципа чистых функций
  • Трудности при тестировании и отладке

Рекомендуемые подходы вместо антипаттернов

  1. Локальное состояние с подъемом при необходимости

    // Паттерн: Контролируемый компонент с поднятым состоянием
    const Table = () => {
      const [expandedRows, setExpandedRows] = useState(new Set());
      
      const toggleRow = useCallback((id) => {
        setExpandedRows(prev => {
          const next = new Set(prev);
          next.has(id) ? next.delete(id) : next.add(id);
          return next;
        });
      }, []);
      
      return data.map(item => (
        <MemoizedRow
          key={item.id}
          isExpanded={expandedRows.has(item.id)}
          onToggle={() => toggleRow(item.id)}
        />
      ));
    };
    
  2. Композиция компонентов и Context API

    // Паттерн: Разделение ответственности
    const Table = () => (
      <TableContext.Provider value={/* общие данные */}>
        <TableHeader />
        <TableBody>
          <TableRows />
          <ExpandedRowsProvider>
            <SubComponents />
          </ExpandedRowsProvider>
        </TableBody>
        <TableFooter />
      </TableContext.Provider>
    );
    
  3. Виртуализация через библиотеки

    import { FixedSizeList as VirtualList } from 'react-window';
    
    const VirtualizedTable = ({ data }) => (
      <VirtualList
        height={500}
        itemCount={data.length}
        itemSize={50}
      >
        {({ index, style }) => (
          <TableRow 
            style={style}
            item={data[index]}
            // Рендерится только 10-15 видимых строк
          />
        )}
      </VirtualList>
    );
    
  4. Оптимизация рендеров через memoization

    const TableRow = React.memo(({ item, onExpand, isExpanded }) => {
      // Оптимизированный компонент
    }, (prevProps, nextProps) => {
      return prevProps.isExpanded === nextProps.isExpanded &&
             prevProps.item.id === nextProps.item.id;
    });
    

Ключевой принцип: управляйте сложностью через композицию, контролируйте рендеры через memoization, обрабатывайте большие данные через виртуализацию. Избегайте преждевременной оптимизации, но заранее проектируйте архитектуру для масштабирования.

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