MUI - 如何以命令式/编程方式打开 Dialog

IT技术 javascript reactjs material-ui react-material
2021-05-25 22:55:23

通常这就是您使用 MUI 的方式Dialog下面的代码取自文档:

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>
  );
}

但我希望它创建Dialog命令式,有点像火和忘记。我不想在需要创建组件时Dialog组件嵌入其他组件中。理想情况下,我想这样称呼它

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

所以我的组件定义看起来像这样

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>
  );
}
1个回答

您可以使用 React 的Provider模式重用对话框React 官方文档已经解释的很详细了,这里就不再赘述了。

Provider在这种情况下,首先创建一个自定义组件,我将调用DialogProvider. 该组件将管理Dialog本地状态s列表

const DialogContext = React.createContext();

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

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

如您所见,我们在这里有一个对话框数组,它包含<Dialog />在渲染时将映射到实际组件的对话框props

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>
  );
}

<DialogContainer/>是的父组件<Dialog/>把任何你想重用的东西放进去这是让您入门的最低示例。

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

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

我们可以setState像往常一样使用创建和删除对话框

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 });
  });
};

但是当我们在这里定义它们时,我们如何在其他组件中调用它们呢?好吧,请记住我们在Provider这里使用了组件,这意味着我们可以向下传递上下文数据,以便其他组件可以引用,在这种情况下,我们希望传递createDialogcloseDialog

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>
);

到这里差不多就完成了,现在我们需要将 加入DialogProvider到组件树中。

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

但是在我们可以使用它们之前,我们应该创建一个钩子来轻松地从父级访问上下文。所以在你的DialogProvider.jsx

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

现在我们可以像这样使用它。

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>
  );
}

现场演示

你可以在这里玩现场演示

编辑 DialogProvider