快速开始

从一个空的 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 文件。

bash
1npm install react-snappy-modal
2# or
3yarn add react-snappy-modal
4# or
5pnpm add react-snappy-modal

2. 添加 SnappyModalProvider

SnappyModalProvider 是已打开模态框的渲染位置。在应用根部附近放置一次即可。模态框渲染在 Provider 的 children 之后,因此会显示在页面之上。

之后,你可以在任何地方调用 SnappyModal.show():组件、事件处理函数,甚至 React 之外的普通工具函数。

main.tsxtsx
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 交给打开它的代码。

库只负责内容定位和背景遮罩,不会给内容添加背景色、内边距或边框。请像下面的示例一样自行设置卡片样式。

ChooseColor.tsxtsx
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。

App.tsxtsx
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 CurrentModalProvideruseCurrentModal() 只能在由 SnappyModal.show() 渲染的组件中使用。把 Hook 移到模态框组件内,或在外部使用 SnappyModal.close(value) 关闭。
模态框内容是透明的内容包装器只负责定位。请为根元素设置背景和内边距,或通过选项传入 className 或 style。
模态框显示在 sticky 头部下方模态框默认使用 z-index: 1000。传入更大的 zIndex 选项,或降低其他元素的 z-index。
模态框后面的页面无法滚动allowScroll 默认为 false,模态框打开期间会固定页面,并在关闭时恢复滚动位置。对于不遮挡页面的面板,请传入 allowScroll: true。
下一篇API 参考 →