React 中使用 Promise 实现可等待的 HTML Dialog 模态框

React 中使用 Promise 实现可等待的 HTML Dialog 模态框

本教程将指导如何在 react 应用中,利用 html 原生 `

` 元素结合 promise 机制,实现一个类似 `window.confirm()` 的异步阻塞式模态框。通过全局状态管理控制模态框的显示与隐藏,并借助 Promise 捕获用户操作结果,从而实现代码的同步等待效果,提升交互逻辑的清晰度。

在 React 中实现可等待的 HTML Dialog 模态框

在现代 Web 应用开发中,模态框(Modal Dialog)是常见的交互组件,用于获取用户输入或显示重要信息。传统的 window.alert()、window.confirm() 或 window.prompt() 提供了阻塞式(blocking)的用户体验,即在用户响应之前,后续代码不会执行。在 React 中,我们通常使用状态(state)来控制模态框的显示与隐藏,但这通常是异步的,无法直接实现类似 window.confirm() 那样的“等待”用户输入的行为。

本教程将展示如何结合 HTML 原生 <dialog> 元素和 javaScript Promise,在 React 中构建一个能够“等待”用户响应的模态框,从而使调用方代码能够以同步的方式处理用户交互结果。

核心思路

实现可等待模态框的关键在于以下两点:

立即学习前端免费学习笔记(深入)”;

  1. 状态管理控制显示/隐藏: 使用 React 状态来控制 <dialog> 元素的 open 属性,从而决定其是否可见。
  2. Promise 机制实现等待: 当模态框打开时,创建一个 Promise 并将其 resolve 函数存储起来。模态框内的用户操作(例如点击“确认”或“取消”按钮)将调用这个存储的 resolve 函数,并传递操作结果,从而使外部的 await 调用得以继续执行。

为了在不同组件间共享模态框的状态和 resolve 函数,我们需要一个简单的全局状态管理方案。

实现步骤

我们将通过一个简单的 createAtom 函数作为全局状态管理器,来演示这一机制。在实际项目中,你可以替换为 Jotai、Zustand、redux Toolkit 或 React Context API 等更成熟的状态管理方案。

1. 定义全局状态管理器 createAtom

createAtom 是一个极简的全局状态管理工具,它允许我们创建可订阅的状态原子。

function createAtom(_def) {   const evt = new EventTarget(); // 用于通知组件状态更新   let _value = _def; // 存储当前状态值    // `use` 钩子,让 React 组件订阅状态变化   function use() {     const [, _refresher] = React.useState(0);     React.useEffect(() => {       const handler = () => _refresher(c => c + 1);       evt.addEventListener('notify', handler);       return () => evt.removeEventListener('notify', handler);     }, []);     return _value;   }    // `setValue` 方法,更新状态并通知所有订阅者   function setValue(v) {     _value = v;     evt.dispatchEvent(new Event('notify'));   }    // `value` 方法,获取当前状态值(不触发组件重新渲染)   const value = () => _value;   return { value, setValue, use }; }  // 定义模态框的打开状态和显示信息 const dialogopen = createAtom(false); // 控制 <dialog> 的 open 属性 const info = createAtom('');          // 用于显示模态框返回的结果信息  // 存储 Promise 的 resolve 函数,以便模态框关闭时调用 let dialogResolve;

这里的 createAtom 模拟了一个非常基础的响应式状态系统。dialogOpen 将控制模态框的可见性,info 用于显示操作结果,而 dialogResolve 则是一个关键变量,它将保存模态框打开时创建的 Promise 的 resolve 函数。

React 中使用 Promise 实现可等待的 HTML Dialog 模态框

可图大模型

可图大模型(Kolors)是快手大模型团队自研打造的文生图AI大模型

React 中使用 Promise 实现可等待的 HTML Dialog 模态框32

查看详情 React 中使用 Promise 实现可等待的 HTML Dialog 模态框

2. 创建 Dialog 组件

Dialog 组件负责渲染 HTML 原生 <dialog> 元素。它的 open 属性由 dialogOpen 状态控制。当用户点击“是”或“取消”按钮时,它会调用之前存储的 dialogResolve 函数,并传递相应的操作结果,同时将 dialogOpen 设置为 false 来关闭模态框。

function Dialog() {   const { use } = dialogOpen; // 使用 dialogOpen 状态   return (     <dialog open={use()}> {/* open 属性控制模态框显示 */}       <p>请选择您的操作:</p>       <button onClick={() => {         dialogResolve('Yes'); // 解决 Promise,传递 'Yes'         dialogOpen.setValue(false); // 关闭模态框       }}>是</button>       <button onClick={() => {         dialogResolve('Cancel'); // 解决 Promise,传递 'Cancel'         dialogOpen.setValue(false); // 关闭模态框       }}>取消</button>     </dialog>   ); }

3. 定义模态框触发函数 modal

modal 函数是外部组件调用模态框的接口。它返回一个 Promise,并在内部执行以下操作:

  • 将 dialogOpen 设置为 true,使模态框显示。
  • 将当前 Promise 的 resolve 函数赋值给全局变量 dialogResolve。
function modal() {   return new Promise((resolve) => {     dialogOpen.setValue(true); // 打开模态框     dialogResolve = resolve;   // 存储 resolve 函数   }); }

4. 创建信息显示组件 Info

这个组件简单地显示 info 状态中的文本,用于演示模态框返回结果。

function Info() {   const txt = info.use();   return <div>{txt}</div>; }

5. 创建测试组件 Test

Test 组件包含一个按钮,点击时调用 modal() 函数。由于 modal() 返回一个 Promise,我们可以使用 await 或 .then() 来等待用户在模态框中的操作结果。

function Test() {   // 在现代 React 环境中,可以直接使用 async/await   const handleClick = async () => {     const response = await modal(); // 等待模态框关闭并返回结果     info.setValue("您点击了 " + response + ' @ ' + new Date().toLocaleTimeString());   };   return (     <div>       <button onClick={handleClick}>触发模态框</button>     </div>   ); }

6. 整合所有组件

最后,将所有组件组合到 Main 组件中,并渲染到 dom

function Main() {   return (     <React.Fragment>       <h1>React Promise Dialog 示例</h1>       <Test/>       <br/>       <Info/>       <Dialog/> {/* 模态框组件需要被渲染 */}     </React.Fragment>   ); }  const root = ReactDOM.createRoot(document.getElementById('root')); root.render(<Main/>);

完整示例代码

以下是包含所有部分的完整代码,可以直接在支持 React 18 的环境中运行:

 <!DOCTYPE html> <html lang="zh-CN"> <head>     <meta charset="UTF-8">     <meta name="viewport" content="width=device-width, initial-scale=1.0">     <title>React Promise Dialog 示例</title>     <script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>     <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>     <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>     <style>         body { font-family: sans-serif; margin: 20px; }         dialog {             border: 1px solid #ccc;             padding: 20px;             border-radius: 8px;             box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);             min-width: 300px;         }         dialog::backdrop {             background: rgba(0, 0, 0, 0.5);         }         button {             margin: 5px;             padding: 8px 15px;             border: none;             border-radius: 4px;

上一篇
下一篇
text=ZqhQzanResources