Работа с FormList

Form.List в Ant Design представляет собой компонент, позволяющий работать с динамическими коллекциями полей формы. Он особенно полезен, когда требуется добавлять, удалять или редактировать множество однотипных элементов, например, списки телефонов, адресов, позиций заказа и других повторяющихся данных.


Основная структура Form.List

<Form form={form} name="dynamic_form">
  <Form.List name="users">
    {(fields, { add, remove }) => (
      <>
        {fields.map(({ key, name, ...restField }) => (
          <Form.Item
            {...restField}
            name={[name, 'first']}
            rules={[{ required: true, message: 'Введите имя' }]}
          >
            <Input placeholder="Имя" />
            <Button onCl ick={() => remove(name)}>Удалить</Button>
          </Form.Item>
        ))}
        <Form.Item>
          <Button type="dashed" onCl ick={() => add()} block>
            Добавить пользователя
          </Button>
        </Form.Item>
      </>
    )}
  </Form.List>
</Form>
  • fields — массив объектов, каждый из которых содержит ключи key, name, fieldKey. Используется для генерации полей формы.
  • add — функция для добавления нового элемента в список.
  • remove — функция для удаления элемента по его индексу.

Динамическое добавление и удаление элементов

Функции add и remove предоставляют возможность контролировать количество элементов в списке:

add({ first: '', last: '' }); // добавляет новый элемент с заданными начальными значениями
remove(index); // удаляет элемент по индексу

Можно передавать объект с начальным значением при добавлении нового элемента, что удобно для форм с предзаполнением.


Валидация элементов списка

Каждое поле внутри Form.List может иметь собственные правила валидации:

<Form.Item
  {...restField}
  name={[name, 'email']}
  rules={[
    { required: true, message: 'Введите email' },
    { type: 'email', message: 'Неверный формат email' }
  ]}
>
  <Input placeholder="Email" />
</Form.Item>

Важно правильно использовать вложенные имена [name, 'field'], чтобы Form корректно связывал данные с их индексом в списке.


Настройка рендеринга списка

Form.List допускает полное управление рендерингом каждого элемента. Можно создавать сложные структуры с несколькими полями:

<Form.List name="addresses">
  {(fields, { add, remove }) => (
    <>
      {fields.map(({ key, name, ...restField }) => (
        <Space key={key} align="baseline">
          <Form.Item
            {...restField}
            name={[name, 'street']}
            rules={[{ required: true, message: 'Введите улицу' }]}
          >
            <Input placeholder="Улица" />
          </Form.Item>
          <Form.Item
            {...restField}
            name={[name, 'city']}
            rules={[{ required: true, message: 'Введите город' }]}
          >
            <Input placeholder="Город" />
          </Form.Item>
          <MinusCircleOutlined onCl ick={() => remove(name)} />
        </Space>
      ))}
      <Form.Item>
        <Button type="dashed" onCl ick={() => add()} icon={<PlusOutlined />}>
          Добавить адрес
        </Button>
      </Form.Item>
    </>
  )}
</Form.List>

Использование Space или других контейнеров позволяет аккуратно выстраивать элементы на странице.


Предзаполнение списка значениями

Для загрузки начальных данных в Form.List используют initialValues:

<Form form={form} initialValues={{
  users: [{ first: 'Иван', last: 'Иванов' }, { first: 'Мария', last: 'Петрова' }]
}}>

Каждый объект массива users автоматически создаст соответствующие поля внутри списка.


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

Form.List отлично подходит для вложенных структур. Например, список заказов с позициями:

<Form.List name="orders">
  {(orderFields, { add: addOrder, remove: removeOrder }) => (
    <>
      {orderFields.map(({ key: orderKey, name: orderName }) => (
        <Card key={orderKey} title={`Заказ ${orderName + 1}`}>
          <Form.List name={[orderName, 'items']}>
            {(itemFields, { add: addItem, remove: removeItem }) => (
              <>
                {itemFields.map(({ key: itemKey, name: itemName }) => (
                  <Space key={itemKey}>
                    <Form.Item name={[itemName, 'product']} rules={[{ required: true }]}>
                      <Input placeholder="Продукт" />
                    </Form.Item>
                    <Form.Item name={[itemName, 'quantity']} rules={[{ required: true }]}>
                      <InputNumber placeholder="Количество" />
                    </Form.Item>
                    <MinusCircleOutlined onCl ick={() => removeItem(itemName)} />
                  </Space>
                ))}
                <Button type="dashed" onCl ick={() => addItem()}>Добавить позицию</Button>
              </>
            )}
          </Form.List>
          <Button type="link" danger onCl ick={() => removeOrder(orderName)}>Удалить заказ</Button>
        </Card>
      ))}
      <Button type="dashed" onCl ick={() => addOrder()}>Добавить заказ</Button>
    </>
  )}
</Form.List>

Такой подход позволяет создавать полностью динамические и вложенные формы без потери контроля над состоянием данных.


Полезные советы при работе с Form.List

  • Всегда использовать уникальные key для элементов списка, особенно при динамическом рендеринге.
  • Для сложных вложенных структур применять name={[index, 'field']} для корректной привязки данных.
  • Для массового обновления значений использовать методы form.setFieldsValue и form.getFieldsValue.
  • Комбинировать с Space, Card и другими контейнерами Ant Design для улучшения визуального представления.

Form.List — мощный инструмент для работы с динамическими данными, позволяющий создавать гибкие формы с минимальным кодом и полной поддержкой валидации.