Is it only me who think that the Modal APIs provided by popular frameworks such as antd (https://ant.design/components/modal/) are awkward and cumbersome to use?

It’s just not intuitive (like confirm() or prompt('input a number')), and how do you show multiple instances of a same type of modal?

I have a trick to display modals in an imperative way.

import React, { useState } from 'react';
import ReactDOM from 'react-dom';
import { Button, Modal } from 'antd';

/*
class Modal extends React.Component<any, any> {
  static ModalTitle = 'TEST';
  ModalButtons = (ctx: any) => [
    <Button onClick={() => ctx.closeModal('123')}>a</Button>,
    <span>a</span>,
  ];
  render() {
    return <div>modal</div>;
  }
}
showModal(Modal, {}).then(r => console.log(r))
or
showModal(
  UserInfoModal,
  { user: currentUser },
  {
    ModalTitle: 'Test',
    ModalButtons: (ctx: any) => [
      <Button onClick={() => ctx.closeModal('123')}>a</Button>,
      <span>a</span>,
    ],
  },
).then(console.log);
 */

function getModalButtons(ModalButtons: any, context: any) {
  if (ModalButtons) {
    return ModalButtons(context);
  }
  const { closeModal } = context;
  return [
    <Button autoFocus onClick={() => closeModal()}>
      Close
    </Button>,
  ];
}

function showModal(
  ModalClass: any,
  props: any = {},
  config: { ModalTitle?: string; ModalButtons?: any } = {},
) {
  return new Promise((resolve) => {
    const modalRoot = document.createElement('div');
    let clearUp: null | (() => void) = null;
    document.body.appendChild(modalRoot);

    const Root = () => {
      const [open, setOpen] = useState(true);
      const [modalButtons, setModalButtons] = useState(null);

      const closeModalFunc = (resolution: any) => {
        resolve(resolution);
        setOpen(false);
        if (clearUp) {
          clearUp();
        }
      };

      const modalInstance = React.createElement(ModalClass, {
        ...(props || {}),

        // for non functional components
        ref: (instance: any) => {
          if (!instance || modalButtons) {
            return;
          }
          setModalButtons(
            getModalButtons(instance.ModalButtons, {
              closeModal: closeModalFunc,
            }),
          );
        },
      });

      return (
        <Modal
          visible={open}
          onCancel={() => closeModalFunc(null)}
          width={(ModalClass as any).ModalWidth || undefined}
          title={(ModalClass as any).ModalTitle || config.ModalTitle || 'Modal'}
          footer={modalButtons}
        >
          {modalInstance}
        </Modal>
      );
    };

    ReactDOM.render(<Root />, modalRoot);

    clearUp = () => {
      setTimeout(() => {
        ReactDOM.unmountComponentAtNode(modalRoot);
        try {
          document.body.removeChild(modalRoot);
        } catch (e) {
          console.warn(e);
        }
      }, 1000);
    };
  });
}

export default showModal;

Actually, I discussed this idea with the author of github.com/eBay/nice-modal-react (before this repo was published), so you can use this library too.