Material-UI-如何命令式/编程式打开对话框 [英] Material-UI - How to open Dialog imperatively/programmatically

查看:51
本文介绍了Material-UI-如何命令式/编程式打开对话框的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

通常,这就是您使用Material-UI的

Normally this is how you use Material-UI's Dialog. The code below is taken from Material-UI's docs

export default function AlertDialog() {
  const [open, setOpen] = React.useState(false);
  const handleClickOpen = () => setOpen(true);
  const handleClose = () => setOpen(false);

  return (
    <div>
      <Button variant="outlined" color="primary" onClick={handleClickOpen}>
        Open Dialog
      </Button>
      <Dialog open={open} onClose={handleClose}>
       {...}
      </Dialog>
    </div>
  );
}

But I want it to create the Dialog imperatively, sort of like fire and forget. I do not want to embed the Dialog component in other components whenever I need to create them. Ideally I'd want to call it like this

createDialog(<>
   <h1>My Dialog</h1>
   <span>My dialog content</span>
   <button onClick={() => closeDialog()}>Close</button>
</>)

So my component definition'd look like this

const createDialog = () => {
   // ???
}
const closeDialog = () => {
   // ???
}
export default function AlertDialog() {
  const [open, setOpen] = React.useState(false);
  const handleClickOpen = () => setOpen(true);
  const handleClose = () => {
     createDialog(<>
        <h1>My Dialog</h1>
        <span>My dialog content</span>
        <button onClick={() => closeDialog()}>Close</button>
     </>)
  };

  return (
    <Button variant="outlined" color="primary" onClick={handleClickOpen}>
      Open Dialog
    </Button>
  );
}

解决方案

You can reuse dialogs using React's Provider pattern. The official React document has explained in good detail so I won't cover it again here.

First create a custom Provider component in this case I'll call DialogProvider. This component will manage a list of Dialogs in local state.

const DialogContext = React.createContext();

export default function DialogProvider({ children }) {
  const [dialogs, setDialogs] = React.useState([]);

  return (
    <DialogContext.Provider {...}>
      {children}
    </DialogContext.Provider>
  );
}

As you can see, we have an array of dialogs here, it contains the dialog props that will be mapped to the actually <Dialog /> component when rendering.

export default function DialogProvider({ children }) {
  const [dialogs, setDialogs] = React.useState([]);

  return (
    <DialogContext.Provider {...}>
      {children}
      {dialogs.map((dialog, i) => {
        return <DialogContainer key={i} {...dialog} />;
      })}
    </DialogContext.Provider>
  );
}

The <DialogContainer/> is the parent component of the <Dialog/>. Put anything that you want to be reusable in there. Here is a minimum example to get you started.

function DialogContainer(props: DialogContainerProps) {
  const { children, open, onClose, onKill } = props;

  return (
    <Dialog open={open} onClose={onClose} onExited={onKill}>
      {children}
    </Dialog>
  );
}

We can create and remove the dialog using setState as normal.

const [dialogs, setDialogs] = React.useState([]);

const createDialog = (option) => {
  const dialog = { ...option, open: true };
  setDialogs((dialogs) => [...dialogs, dialog]);
};

const closeDialog = () => {
  setDialogs((dialogs) => {
    const latestDialog = dialogs.pop();
    if (!latestDialog) return dialogs;
    if (latestDialog.onClose) latestDialog.onClose();
    return [...dialogs].concat({ ...latestDialog, open: false });
  });
};

But how do we call them in other components when we defined them here? Well, remember we're using Provider component here, which means we can pass the context data down so other components can reference, in this case we want to pass the createDialog and closeDialog down.

const [dialogs, setDialogs] = React.useState([]);
const createDialog = (option) => {/*...*/};
const closeDialog = () => {/*...*/};
const contextValue = React.useRef([createDialog, closeDialog]);

return (
  <DialogContext.Provider value={contextValue.current}>
    {children}
    {dialogs.map((dialog, i) => ...)}
  </DialogContext.Provider>
);

We're almost done here, now we need to add the DialogProvider to the component tree.

export default function App() {
  return (
    <DialogProvider>
      <App {...} />
    </DialogProvider>
  );
}

But before we can use them, we should create a hook to easily access the context from the parent. So in your DialogProvider.jsx

export const useDialog = () => React.useContext(DialogContext);

Now we can use it like this.

import { useDialog } from "./DialogProvider";

export default function Content() {
  const [openDialog, closeDialog] = useDialog();
  const onOpenDialog = () => {
    openDialog({
      children: (
        <>
          <DialogTitle>This dialog is opened imperatively</DialogTitle>
          <DialogContent>Some content</DialogContent>
          <DialogActions>
            <Button color="primary" onClick={closeDialog}>Close</Button>
          </DialogActions>
        </>
      )
    });
  };

  return (
    <Button variant="contained" onClick={onOpenDialog}>
      Show dialog
    </Button>
  );
}

Live Demo

You can play around in the live demo here

这篇关于Material-UI-如何命令式/编程式打开对话框的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆