Вложенные модальные окна

В библиотеке Ant Design модальные окна реализованы через компонент Modal, который обеспечивает гибкое управление отображением, анимацией и взаимодействием с пользователем. Основное свойство для управления видимостью модального окна — visible (или open в последних версиях). Для закрытия используется колбэк onCancel, а для подтверждения действий — onOk.

import { Modal, Button } from 'antd';
import { useState } from 'react';

const App = () => {
  const [isModalOpen, setIsModalOpen] = useState(false);

  const showModal = () => setIsModalOpen(true);
  const handleOk = () => setIsModalOpen(false);
  const handleCancel = () => setIsModalOpen(false);

  return (
    <>
      <Button type="primary" onCl ick={showModal}>Открыть модальное окно</Button>
      <Modal
        title="Простое модальное окно"
        open={isModalOpen}
        onOk={handleOk}
        onCan cel={handleCancel}
      >
        <p>Содержимое модального окна</p>
      </Modal>
    </>
  );
};

Вложенные модальные окна: концепция

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

В Ant Design каждый Modal является независимым компонентом, поэтому можно управлять их состояниями через отдельные состояния React. При этом важно учитывать:

  • Приоритет модальных окон: модальное окно, открытое поверх другого, должно иметь больший z-index, чтобы не перекрываться нижним.
  • Управление фокусом: автоматическая установка фокуса на активное окно.
  • Закрытие родительского окна: если родитель закрывается, вложенные окна также должны быть корректно закрыты.

Пример вложенных модальных окон

import { Modal, Button } from 'antd';
import { useState } from 'react';

const NestedModals = () => {
  const [isParentOpen, setIsParentOpen] = useState(false);
  const [isChildOpen, setIsChildOpen] = useState(false);

  const openParent = () => setIsParentOpen(true);
  const closeParent = () => {
    setIsParentOpen(false);
    setIsChildOpen(false); // закрываем вложенные окна вместе с родителем
  };

  const openChild = () => setIsChildOpen(true);
  const closeChild = () => setIsChildOpen(false);

  return (
    <>
      <Button type="primary" onCl ick={openParent}>Открыть родительское окно</Button>
      <Modal
        title="Родительское окно"
        open={isParentOpen}
        onCan cel={closeParent}
        footer={[
          <Button key="openChild" type="primary" onCl ick={openChild}>
            Открыть вложенное окно
          </Button>,
          <Button key="closeParent" onCl ick={closeParent}>
            Закрыть
          </Button>
        ]}
      >
        <p>Контент родительского окна</p>
      </Modal>

      <Modal
        title="Вложенное окно"
        open={isChildOpen}
        onCan cel={closeChild}
        footer={[
          <Button key="closeChild" onCl ick={closeChild}>
            Закрыть вложенное окно
          </Button>
        ]}
      >
        <p>Контент вложенного окна</p>
      </Modal>
    </>
  );
};

Особенности работы с вложенными окнами

  1. Асинхронные операции: часто вложенные модальные окна используются для подтверждения действий или ввода данных. Рекомендуется использовать async/await внутри обработчиков onOk для корректного закрытия и обработки результата.
const handleChildOk = async () => {
  await performAction();
  setIsChildOpen(false);
};
  1. Управление фокусом и доступностью: Modal автоматически блокирует фон и фокусирует первый элемент внутри. При вложенных модальных окнах нужно следить, чтобы фокус корректно возвращался на родительский компонент после закрытия дочернего окна.

  2. Стилизация и позиционирование: Ant Design корректно управляет z-index, но при сложных вложенных структурах можно явно использовать style={{ zIndex: 1050 }} или свойство getContainer={false} для рендеринга внутри родительского DOM-узла.

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

Продвинутые техники

  • Динамическое создание модалей: через метод Modal.confirm() или Modal.info() можно создавать модальные окна программно, не включая их в JSX напрямую. Это полезно для вложенных модальных окон, когда их содержание зависит от предыдущего окна.
import { Modal } from 'antd';

const showConfirm = () => {
  Modal.confirm({
    title: 'Подтверждение действия',
    content: 'Вы уверены, что хотите продолжить?',
    onOk() {
      console.log('Действие подтверждено');
    }
  });
};
  • Передача данных между окнами: родительское окно может передавать данные в дочернее через пропсы, а дочернее — возвращать результаты через callback-функции. Это снижает необходимость глобального состояния для простых вложенных сценариев.
<ChildModal
  visible={isChildOpen}
  data={parentData}
  onCl ose={closeChild}
  onSub mit={handleChildSubmit}
/>
  • Модальные с формами: сочетание Modal и Form позволяет создавать многоступенчатые диалоги, где каждое вложенное окно собирает часть данных, а итоговый результат отправляется после закрытия всех окон.
<Modal open={isChildOpen} onOk={form.submit} onCan cel={closeChild}>
  <Form form={form} onFin ish={handleSubmit}>
    <Form.Item name="field1" label="Поле 1" rules={[{ required: true }]}>
      <Input />
    </Form.Item>
  </Form>
</Modal>

Рекомендации по проектированию вложенных модальных окон

  • Минимизировать глубину вложенности — 2–3 уровня максимум.
  • Использовать программное создание модалей для временных окон и предупреждений.
  • Контролировать закрытие родителя, чтобы не оставлять «висящие» дочерние окна.
  • Разделять визуальные и функциональные слои: родитель управляет состоянием, дети выполняют задачи, возвращая результат.

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