Динамические поля форм

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


Основы динамических полей

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

Простейший пример структуры:

import { Form, Input, Button } from 'antd';
import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons';

<Form name="dynamic_form" autoComplete="off">
  <Form.List name="users">
    {(fields, { add, remove }) => (
      <>
        {fields.map(({ key, name, fieldKey, ...restField }) => (
          <Form.Item
            key={key}
            required={false}
          >
            <Form.Item
              {...restField}
              name={[name, 'firstName']}
              fieldKey={[fieldKey, 'firstName']}
              rules={[{ required: true, message: 'Введите имя' }]}
              noStyle
            >
              <Input placeholder="Имя" style={{ width: '60%' }} />
            </Form.Item>
            <MinusCircleOutlined onCl ick={() => remove(name)} />
          </Form.Item>
        ))}
        <Form.Item>
          <Button type="dashed" onCl ick={() => add()} icon={<PlusOutlined />}>
            Добавить пользователя
          </Button>
        </Form.Item>
      </>
    )}
  </Form.List>
</Form>

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

  • Form.List принимает имя поля (name), которое будет использоваться в объекте формы.
  • Функция рендеринга получает массив fields и методы add, remove, которые управляют динамическими элементами.
  • Каждое поле должно иметь уникальный key для корректной работы React.

Управление валидацией динамических полей

Динамические поля полностью поддерживают систему валидации Ant Design. Важно правильно указывать правила валидации через rules, особенно при вложенных объектах:

<Form.List name="emails">
  {(fields, { add, remove }) => (
    <>
      {fields.map(({ key, name, fieldKey, ...restField }) => (
        <Form.Item key={key} required={false}>
          <Form.Item
            {...restField}
            name={[name, 'email']}
            fieldKey={[fieldKey, 'email']}
            rules={[
              { required: true, message: 'Введите email' },
              { type: 'email', message: 'Неверный формат email' }
            ]}
            noStyle
          >
            <Input placeholder="Email" style={{ width: '60%' }} />
          </Form.Item>
          <MinusCircleOutlined onCl ick={() => remove(name)} />
        </Form.Item>
      ))}
      <Form.Item>
        <Button type="dashed" onCl ick={() => add()} icon={<PlusOutlined />}>
          Добавить email
        </Button>
      </Form.Item>
    </>
  )}
</Form.List>

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


Динамические поля с вложенными структурами

Ant Design поддерживает вложенные динамические списки, что позволяет создавать сложные формы, например, список пользователей с несколькими телефонами:

<Form.List name="users">
  {(userFields, { add: addUser, remove: removeUser }) => (
    <>
      {userFields.map(({ key: userKey, name: userName, fieldKey: userFieldKey, ...userRestField }) => (
        <div key={userKey} style={{ marginBottom: 20 }}>
          <Form.Item
            {...userRestField}
            name={[userName, 'name']}
            fieldKey={[userFieldKey, 'name']}
            rules={[{ required: true, message: 'Введите имя' }]}
          >
            <Input placeholder="Имя пользователя" />
          </Form.Item>
          <Form.List name={[userName, 'phones']}>
            {(phoneFields, { add: addPhone, remove: removePhone }) => (
              <>
                {phoneFields.map(({ key, name, fieldKey, ...restField }) => (
                  <Form.Item key={key}>
                    <Form.Item
                      {...restField}
                      name={name}
                      fieldKey={fieldKey}
                      rules={[{ required: true, message: 'Введите телефон' }]}
                      noStyle
                    >
                      <Input placeholder="Телефон" style={{ width: '60%' }} />
                    </Form.Item>
                    <MinusCircleOutlined onCl ick={() => remove(name)} />
                  </Form.Item>
                ))}
                <Form.Item>
                  <Button type="dashed" onCl ick={() => addPhone()} icon={<PlusOutlined />}>
                    Добавить телефон
                  </Button>
                </Form.Item>
              </>
            )}
          </Form.List>
          <MinusCircleOutlined onCl ick={() => removeUser(userName)} />
        </div>
      ))}
      <Form.Item>
        <Button type="dashed" onCl ick={() => addUser()} icon={<PlusOutlined />}>
          Добавить пользователя
        </Button>
      </Form.Item>
    </>
  )}
</Form.List>

Настройка отображения и стилей

  • Использование noStyle позволяет комбинировать вложенные Form.Item без лишних отступов.
  • Компоненты Input, Select, DatePicker и другие можно гибко комбинировать внутри Form.List.
  • С помощью CSS и style легко управлять шириной, отступами и расположением кнопок добавления/удаления элементов.

Работа с начальными значениями

Динамические поля полностью поддерживают свойство initialValues у Form. Для этого нужно передавать массив объектов с нужной структурой:

<Form
  name="dynamic_initial"
  initialValues={{
    users: [
      { name: 'Иван', phones: ['+7 123 456 78 90'] },
      { name: 'Мария', phones: ['+7 987 654 32 10'] }
    ]
  }}
>

Ant Design автоматически сгенерирует соответствующее количество полей и заполнит их значениями.


Поддержка удаления всех полей и динамического управления

Методы add и remove позволяют реализовать:

  • Полное удаление всех полей (fields.forEach(f => remove(f.name)))
  • Добавление новых элементов с предзаданными значениями (add({ name: '', phones: [''] }))
  • Манипуляции с индексами и ключами для синхронизации с внешними данными

Особенности производительности

При работе с большим количеством динамических полей рекомендуется:

  • Использовать уникальные key для каждого элемента, особенно при рендере массивов.
  • Применять shouldUpdate или React.memo для вложенных компонентов, чтобы избежать лишнего ререндеринга.
  • Для очень больших списков можно рассмотреть виртуализацию, например с помощью react-window.

Итоговые рекомендации по использованию

  • Form.List — основной инструмент для динамических полей в Ant Design.
  • Методы add и remove обеспечивают полный контроль над структурой данных.
  • Валидация, начальные значения и вложенные списки работают полностью автоматически.
  • Продуманное управление стилями и уникальными ключами обеспечивает стабильную работу формы даже при сотнях динамических элементов.