快速开始
从一个空的 React 应用开始,做出一个能把用户选择返回给打开它的代码的模态框。大约需要五分钟。
环境要求
除 React 外,React Snappy Modal 没有运行时依赖。包将 React 16.12、17 和 18 声明为 peer dependency,本文档站点运行在 React 19 上。
模态框通过 SnappyModal.show() 打开,并通过完成 Promise 关闭,因此最好熟悉 async 和 await。包内已包含 TypeScript 类型声明,无需单独安装 @types 包。
1. 安装包
使用你的包管理器安装。样式表已包含在包中,导入 react-snappy-modal 时会自动应用,无需额外添加 CSS 文件。
1npm install react-snappy-modal
2# or
3yarn add react-snappy-modal
4# or
5pnpm add react-snappy-modal2. 添加 SnappyModalProvider
SnappyModalProvider 是已打开模态框的渲染位置。在应用根部附近放置一次即可。模态框渲染在 Provider 的 children 之后,因此会显示在页面之上。
之后,你可以在任何地方调用 SnappyModal.show():组件、事件处理函数,甚至 React 之外的普通工具函数。
1import { createRoot } from 'react-dom/client';
2import { SnappyModalProvider } from 'react-snappy-modal';
3import App from './App';
4
5createRoot(document.getElementById('root')!).render(
6 <SnappyModalProvider>
7 <App />
8 </SnappyModalProvider>,
9);3. 编写模态框组件
模态框就是普通的 React 组件。在组件内调用 useCurrentModal() 获取 resolveModal。调用 resolveModal(value) 会关闭当前这个模态框,并把 value 交给打开它的代码。
库只负责内容定位和背景遮罩,不会给内容添加背景色、内边距或边框。请像下面的示例一样自行设置卡片样式。
1import { useCurrentModal } from 'react-snappy-modal';
2
3export function ChooseColor() {
4 const { resolveModal } = useCurrentModal();
5
6 return (
7 <div style={{ background: 'white', padding: 24, borderRadius: 12 }}>
8 <h2>Pick a color</h2>
9 <button onClick={() => resolveModal('red')}>Red</button>
10 <button onClick={() => resolveModal('blue')}>Blue</button>
11 <button onClick={() => resolveModal(null)}>Cancel</button>
12 </div>
13 );
14}4. 打开模态框并等待结果
SnappyModal.show(element, options?) 渲染元素并返回 Promise。函数会在 await 处暂停,直到模态框关闭,然后带着结果继续执行。无需连接 isOpen 状态或回调 prop。
1import { useState } from 'react';
2import SnappyModal from 'react-snappy-modal';
3import { ChooseColor } from './ChooseColor';
4
5export default function App() {
6 const [color, setColor] = useState('none');
7
8 const pickColor = async () => {
9 const result = await SnappyModal.show(<ChooseColor />);
10 // Runs only after the modal is closed
11 if (result) setColor(result);
12 };
13
14 return <button onClick={pickColor}>Selected: {color}</button>;
15}Promise 返回什么
结果取决于模态框的关闭方式。
| 关闭方式 | 结果 |
|---|---|
resolveModal(value) 或 SnappyModal.close(value) | 以 value resolve |
点击背景(allowOutsideClick 为默认值 true 时) | 以 undefined resolve |
rejectModal(error) 或 SnappyModal.throw(error) | 以 error reject,需用 try/catch 处理 |
工作原理
在内部,一次调用会经历四个步骤。
show()把模态框加入内部栈,分配唯一的modalId,并返回一个等待中的 Promise。SnappyModalProvider重新渲染,按layer顺序绘制栈中的所有模态框。- 组件调用
resolveModal()后,模态框从栈中移除,Promise 完成。 await之后的代码拿到结果继续执行。
问题排查
大多数配置问题属于以下情况之一。
| 现象 | 原因与解决 |
|---|---|
什么都没显示,await 之后的代码也不执行 | SnappyModalProvider 没有挂载。确认 Provider 包裹了当前渲染的树。模态框关闭前,Promise 会一直处于等待状态。 |
| Error: useCurrentModal must be used within CurrentModalProvider | useCurrentModal() 只能在由 SnappyModal.show() 渲染的组件中使用。把 Hook 移到模态框组件内,或在外部使用 SnappyModal.close(value) 关闭。 |
| 模态框内容是透明的 | 内容包装器只负责定位。请为根元素设置背景和内边距,或通过选项传入 className 或 style。 |
| 模态框显示在 sticky 头部下方 | 模态框默认使用 z-index: 1000。传入更大的 zIndex 选项,或降低其他元素的 z-index。 |
| 模态框后面的页面无法滚动 | allowScroll 默认为 false,模态框打开期间会固定页面,并在关闭时恢复滚动位置。对于不遮挡页面的面板,请传入 allowScroll: true。 |