发布于 2026-01-05 2 阅读
0

React 中处理警告对话框的最简洁方法🥰

React 中处理警告对话框的最简洁方法🥰

阅读时间——5分钟☕️

大家好!我是个懒惰的开发者,今天我们来聊聊如何在 React 中轻松处理对话框提示😢。如果你已经厌倦了为了创建一个该死的“一个问题”模态对话框而进行大量的复制粘贴——那就准备好咖啡,我们开始吧。

目标

我们想找到一种最简洁的提示框显示方案,类似于浏览器自带的提示alert框功能。



const isConfirmed = alert("Are you sure you want to remove this burrito?");

if (isConfirmed) {
  await api.deleteThisAwfulBurrito();
}


Enter fullscreen mode Exit fullscreen mode

抢先看

最终我们会得到类似这样的结果。



const YourAwesomeComponent = () => {
  const confirm = useConfirmation()

  confirm({
    variant: "danger",
    title: "Are you sure you want to remove this burrito?",
    description: "If you will remove this burrito you will regret it 😡!!"
  }).then(() => {
    api.deleteThisAwfulBurrito();
  });
}



Enter fullscreen mode Exit fullscreen mode

有兴趣吗?我们来写点代码吧。

首先,我们需要从创建模态对话框开始。这是一个用❤️和material-ui构建的简单警告对话框。



import {
  Button,
  Dialog,
  DialogTitle,
  DialogContent,
  DialogContentText,
  DialogActions,
} from "@material-ui/core";

export const ConfirmationDialog = ({
  open,
  title,
  variant,
  description,
  onSubmit,
  onClose
}) => {
  return (
    <Dialog open={open}>
      <DialogTitle>{title}</DialogTitle>
      <DialogContent>
        <DialogContentText>{description}</DialogContentText>
      </DialogContent>
      <DialogActions>
        <Button color="primary" onClick={onSubmit}>
          YES, I AGREE
        </Button>
        <Button color="primary" onClick={onClose} autoFocus>
          CANCEL
        </Button>
      </DialogActions>
    </Dialog>
  );
};



Enter fullscreen mode Exit fullscreen mode

好的,但是我们要如何让它动态运行呢?这是一个值得思考的问题。如果用户一次只能看到一个提示对话框,为什么每个组件都需要那么多对话框呢?

如果你在一个警告对话框之上显示另一个警告对话框……你可能需要重新考虑一下应用程序的用户体验部分。
神啊,请不要有任何图像

好了,我们开始吧。我们只需要在应用程序根目录渲染一个顶级模态框,并在需要时显示它。我们将利用 React Hooks 的强大功能,让它看起来更加优雅。

包裹上下文

让我们创建一个新的上下文实例,并将组件树包裹在其中。此外,创建一个简单的状态,用于保存当前显示的警报选项(例如标题、描述以及您需要的所有内容)。



interface ConfirmationOptions {
  title: string;
  description: string;
}

const ConfirmationServiceContext = React.createContext<
  // we will pass the openning dialog function directly to consumers
  (options: ConfirmationOptions) => Promise<void>
>(Promise.reject);

export const ConfirmationServiceProvider= ({ children }) => {
  const [
    confirmationState,
    setConfirmationState
  ] = React.useState<ConfirmationOptions | null>(null);

  const openConfirmation = (options: ConfirmationOptions) => {
    setConfirmationState(options);
    return Promise.resolve()
  };

  return (
    <>
      <ConfirmationServiceContext.Provider
        value={openConfirmation}
        children={children}
      />

      <Dialog open={Boolean(confirmationState)} {...confirmationState} />
    </>
  );
};



Enter fullscreen mode Exit fullscreen mode

现在,只要我们连接任何消费者并调用提供的函数,我们的对话框就会打开。

解决确认

现在我们需要处理对话框关闭和接收用户回调的问题。这里使用了Promise基于 API 的方法,但也可以使用回调的方式实现。在这个例子中,一旦用户接受或取消了提示框,等待的 Promise 就会被解析或拒绝。

为此,我们需要保存Promise解析函数,并在适当的用户操作时调用它们。React 的 `resolve` 类ref是最佳选择。



  const awaitingPromiseRef = React.useRef<{
    resolve: () => void;
    reject: () => void;
  }>();

  const openConfirmation = (options: ConfirmationOptions) => {
    setConfirmationState(options);
    return new Promise((resolve, reject) => {
      // save the promise result to the ref
      awaitingPromiseRef.current = { resolve, reject };
    });
  };

  const handleClose = () => {
    // Mostly always you don't need to handle canceling of alert dialog
    // So shutting up the unhandledPromiseRejection errors 
    if (confirmationState.catchOnCancel && awaitingPromiseRef.current) {
      awaitingPromiseRef.current.reject();
    }

    setConfirmationState(null);
  };

  const handleSubmit = () => {
    if (awaitingPromiseRef.current) {
      awaitingPromiseRef.current.resolve();
    }

    setConfirmationState(null);
  };



Enter fullscreen mode Exit fullscreen mode

好了!我们的对话机器就快完成了!还剩最后一件事——创建一个自定义钩子以提高可读性。



export const useConfirmationService = () =>
  React.useContext(ConfirmationServiceContext);


Enter fullscreen mode Exit fullscreen mode

定制

您可以通过传递额外的variant属性轻松自定义对话框内容。只需将其添加到……ConfirmationOptions



export interface ConfirmationOptions {
  variant: "danger" | "info";
  title: string;
  description: string;
}



Enter fullscreen mode Exit fullscreen mode

您可以根据需要渲染不同的对话框内容。



<DialogActions>
{variant === "danger" && (
<>
<Button color="primary" onClick={onSubmit}>
Yes, I agree
</Button>
<Button color="primary" onClick={onClose} autoFocus>
CANCEL
</Button>
</>
)}

<span class="si">{</span><span class="nx">variant</span> <span class="o">===</span> <span class="dl">"</span><span class="s2">info</span><span class="dl">"</span> <span class="o">&amp;&amp;</span> <span class="p">(</span>
  <span class="p">&lt;</span><span class="nc">Button</span> <span class="na">color</span><span class="p">=</span><span class="s">"primary"</span> <span class="na">onClick</span><span class="p">=</span><span class="si">{</span><span class="nx">onSubmit</span><span class="si">}</span><span class="p">&gt;</span>
    OK
  <span class="p">&lt;/</span><span class="nc">Button</span><span class="p">&gt;</span>
<span class="p">)</span><span class="si">}</span>
Enter fullscreen mode Exit fullscreen mode

</DialogActions>

Enter fullscreen mode Exit fullscreen mode




准备好了吗?!

孩子们,你们准备好了吗?!

这是最终的可运行示例。如果你愿意,可以直接借鉴文件的实现ConfirmationService.tsx。这部分逻辑与我们讨论的内容相当独立。

PS:本文撰写过程中没有墨西哥卷饼受到伤害

文章来源:https://dev.to/dmtrkovalenko/the-neatest-way-to-handle-alert-dialogs-in-react-1aoe