【问题标题】:MUI - How to open Dialog imperatively/programmaticallyMUI - 如何以命令式/编程方式打开对话框
【发布时间】:2020-12-23 12:16:30
【问题描述】:

通常这是您使用 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>
  );
}

【问题讨论】:

    标签: javascript reactjs material-ui react-material


    【解决方案1】:

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

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

    const DialogContext = React.createContext();
    
    export default function DialogProvider({ children }) {
      const [dialogs, setDialogs] = React.useState([]);
    
      return (
        <DialogContext.Provider {...}>
          {children}
        </DialogContext.Provider>
      );
    }
    

    如您所见,我们这里有一个对话框数组,其中包含在渲染时将映射到实际&lt;Dialog /&gt; 组件的对话框道具。

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

    &lt;DialogContainer/&gt;&lt;Dialog/&gt; 的父组件。 将您希望可重复使用的任何东西放在那里。这是一个让您入门的最小示例。

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

    现场演示

    你可以在这里玩现场演示

    【讨论】:

    猜你喜欢
    • 2019-01-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-16
    相关资源
    最近更新 更多