Вложенные структуры данных

Ant Design предоставляет мощные компоненты для отображения и управления сложными, вложенными структурами данных. Наиболее часто применяемые компоненты для этих целей — Tree, Table с expandable rows, Cascader и Form с динамическими полями. Правильное использование этих компонентов позволяет создавать удобные интерфейсы для работы с иерархическими данными и вложенными объектами.


Компонент Tree

Tree используется для отображения иерархических данных в виде дерева. Основные возможности:

  • Отображение узлов с дочерними элементами.
  • Поддержка выбора, чекбоксов и выделения активного элемента.
  • Асинхронная подгрузка дочерних элементов.

Пример структуры данных для Tree:

const treeData = [
  {
    title: 'Главный узел',
    key: '0',
    children: [
      {
        title: 'Дочерний узел 1',
        key: '0-0',
        children: [
          { title: 'Лист 1-0', key: '0-0-0' },
          { title: 'Лист 1-1', key: '0-0-1' },
        ],
      },
      { title: 'Дочерний узел 2', key: '0-1' },
    ],
  },
];

Подключение Tree:

import { Tree } from 'antd';

<Tree
  treeData={treeData}
  defaultExpandAll
  checkable
  onSel ect={(selectedKeys, info) => console.log(selectedKeys, info)}
  onCh eck={(checkedKeys, info) => console.log(checkedKeys, info)}
/>

Ключевые моменты:

  • treeData — массив объектов с полями title, key и children.
  • checkable включает возможность выбора элементов через чекбоксы.
  • defaultExpandAll автоматически раскрывает все узлы дерева.

Table с expandable rows

Для отображения вложенных объектов в таблицах используется свойство expandedRowRender, позволяющее вложить одну таблицу в другую. Структура данных может содержать массивы объектов внутри каждого элемента:

const data = [
  {
    key: 1,
    name: 'Проект A',
    tasks: [
      { id: 1, title: 'Задача A1', status: 'В процессе' },
      { id: 2, title: 'Задача A2', status: 'Завершена' },
    ],
  },
  {
    key: 2,
    name: 'Проект B',
    tasks: [
      { id: 3, title: 'Задача B1', status: 'В процессе' },
    ],
  },
];

Пример реализации:

import { Table } from 'antd';

const columns = [
  { title: 'Название проекта', dataIndex: 'name', key: 'name' },
];

<Table
  columns={columns}
  dataSource={data}
  expandable={{
    expandedRowRender: record => (
      <Table
        columns={[
          { title: 'Задача', dataIndex: 'title', key: 'title' },
          { title: 'Статус', dataIndex: 'status', key: 'status' },
        ]}
        dataSource={record.tasks}
        pagination={false}
      />
    ),
  }}
/>

Особенности:

  • expandedRowRender может быть функцией, возвращающей JSX с любой структурой.
  • Вложенные таблицы полностью поддерживают сортировку и фильтры, что позволяет строить сложные интерфейсы управления данными.

Cascader для вложенного выбора

Cascader подходит для выбора из вложенных опций, где каждая последующая зависима от предыдущей. Типичный пример — выбор страны, региона и города:

const options = [
  {
    value: 'russia',
    label: 'Россия',
    children: [
      {
        value: 'moscow',
        label: 'Москва',
        children: [
          { value: 'tverskaya', label: 'Тверская' },
        ],
      },
      {
        value: 'spb',
        label: 'Санкт-Петербург',
      },
    ],
  },
  {
    value: 'usa',
    label: 'США',
    children: [
      {
        value: 'ny',
        label: 'Нью-Йорк',
      },
    ],
  },
];

Использование:

import { Cascader } from 'antd';

<Cascader
  options={options}
  placeholder="Выберите регион"
/>

Особенности:

  • Поддерживает динамическую загрузку данных через loadData.
  • Можно настраивать поведение выбора и отображение только конечных узлов через changeOnSelect.

Form с динамическими полями

Для управления вложенными объектами и массивами данных идеально подходит Form.List. Это позволяет создавать формы с повторяющимися или вложенными элементами.

Пример:

import { Form, Input, Button } from 'antd';

<Form>
  <Form.List name="projects">
    {(fields, { add, remove }) => (
      <>
        {fields.map(field => (
          <div key={field.key} style={{ marginBottom: 8 }}>
            <Form.Item
              {...field}
              name={[field.name, 'name']}
              fieldKey={[field.fieldKey, 'name']}
              rules={[{ required: true, message: 'Введите название проекта' }]}
            >
              <Input placeholder="Название проекта" />
            </Form.Item>
            <Form.List name={[field.name, 'tasks']}>
              {(taskFields, { add: addTask, remove: removeTask }) => (
                <>
                  {taskFields.map(taskField => (
                    <Form.Item
                      key={taskField.key}
                      {...taskField}
                      name={[taskField.name, 'title']}
                      fieldKey={[taskField.fieldKey, 'title']}
                      rules={[{ required: true, message: 'Введите название задачи' }]}
                    >
                      <Input placeholder="Название задачи" />
                    </Form.Item>
                  ))}
                  <Button onCl ick={() => addTask()}>Добавить задачу</Button>
                </>
              )}
            </Form.List>
            <Button onCl ick={() => remove(field.name)}>Удалить проект</Button>
          </div>
        ))}
        <Button onCl ick={() => add()}>Добавить проект</Button>
      </>
    )}
  </Form.List>
</Form>

Особенности:

  • Позволяет управлять массивами и вложенными объектами в форме без ручной генерации ключей.
  • Совместим с валидацией, динамическим добавлением и удалением элементов.
  • Хорошо сочетается с компонентами Ant Design, такими как Select, DatePicker и InputNumber.

Лучшие практики при работе с вложенными структурами

  1. Уникальные ключи: каждый узел и элемент таблицы должен иметь уникальный key, чтобы React корректно отслеживал изменения.
  2. Разделение логики: для больших вложенных структур лучше создавать отдельные компоненты для каждого уровня вложенности.
  3. Асинхронная подгрузка: при больших объёмах данных используйте loadData в Tree или expandedRowRender с динамическим запросом, чтобы улучшить производительность.
  4. Использование Form.List: для форм с повторяющимися вложенными элементами это оптимальный вариант, позволяющий избегать ручного управления состоянием массивов.
  5. Оптимизация ререндеров: при глубокой вложенности компонентов рекомендуется использовать React.memo для дочерних элементов и избегать ненужного пересоздания массивов данных.

Эти подходы позволяют создавать интерфейсы, где сложные вложенные структуры данных визуализируются удобно, интуитивно и с полной поддержкой динамического взаимодействия. Комбинирование Tree, Table, Cascader и Form.List обеспечивает гибкость при работе с иерархиями и вложенными объектами в проектах на Ant Design.