react-native-promise-portal:React Native 弹窗管理的新思路


在 React Native 开发中,我们经常需要弹出对话框、提示框、日期选择器或者其他 overlay 组件。传统方式通常依赖 state + conditional rendering + 回调函数,但在复杂场景下,代码会变得分散、难维护,尤其是当多个弹窗同时存在时。

react-native-promise-portal 提供了一种 Promise + Portal 的解决方案,让你可以像调用普通异步函数一样调用弹窗,同时保持逻辑线性、易于管理。

核心特点

  1. Promise-first 弹窗调用

    弹窗调用返回 Promise,你可以用 await 直接获取用户操作结果。业务逻辑与 UI 逻辑解耦,写出来的代码更直观。

  2. 支持多重弹窗 & name 去重

    • 可以在同一页面同时显示多个弹窗,每个弹窗独立关闭。
    • 通过 index 控制渲染层级,index 越大,显示在最上层。
    • 通过 name 去重,避免重复弹窗触发业务冲突。
  3. 局部 Portal

    • 支持在特定页面或模块内管理弹窗,不影响全局 Portal 状态。
    • 跨组件调用弹窗,保证弹窗只在目标页面显示。
    • 避免不同页面弹窗相互覆盖或冲突,提升模块化管理能力。
  4. 脱离 Hook 调用局部 UI

    • 使用 PortalManager 可以在 非组件上下文 触发局部弹窗。
    • 适用于后台逻辑、网络请求回调、定时任务等场景。
    • Promise 接口仍然保持逻辑线性。
  5. 基于 Portal 渲染

    • 利用 React Native 的 Portal 技术,将弹窗渲染到顶层,解决 z-index 和 clipping 问题。

基本使用

1. 设置 PortalProvider

tsx 复制代码
import { PortalProvider } from 'react-native-promise-portal';

export default function RootLayout() {
  return <PortalProvider>{/* Your app content */}</PortalProvider>;
}

2. 使用 Hook 调用弹窗

tsx 复制代码
import { usePortal } from 'react-native-promise-portal';

function MyComponent() {
  const { showWithOverlay } = usePortal();

  const handleShowDialog = async () => {
    try {
      const result = await showWithOverlay<boolean>({
        component: ({ close }) => (
          <Confirm title="Confirm" subTitle="Are you sure?" close={close} />
        ),
      });
      console.log('Result:', result);
    } catch (error) {
      console.error(error);
    }
  };

  return <Button onPress={handleShowDialog} title="Show Dialog" />;
}

多重弹窗与 name 去重示例

tsx 复制代码
const { showWithOverlay } = usePortal();

// 弹出第一个弹窗
await showWithOverlay({
  name: 'confirm-delete',
  component: ({ close }) => <Confirm close={close} />,
});

// 再次调用同名弹窗会抛出 PortalAlreadyExistsError
await showWithOverlay({
  name: 'confirm-delete',
  component: ({ close }) => <Confirm close={close} />,
});

通过 index 可以控制层级顺序:

tsx 复制代码
const promise1 = showWithOverlay({ title: 'Dialog 1', index: 1 });
const promise2 = showWithOverlay({ title: 'Dialog 2', index: 10 }); // 更高层级
const promise3 = showWithOverlay({ title: 'Dialog 3', index: 5 });

Promise.allSettled([promise1, promise2, promise3]).then((results) => {
  console.log('All dialogs closed:', results);
});

局部 Portal 解决的业务痛点

在大型应用中,某些弹窗只在特定页面显示,使用全局 Portal 管理容易导致以下问题:

  • 弹窗状态全局混乱
  • 不同页面弹窗可能相互覆盖
  • 逻辑与 UI 耦合,调用分散

局部 Portal 通过 PortalManager 提供页面级管理:

tsx 复制代码
import { PortalRender, PortalManager } from 'react-native-promise-portal';
import { useLocalPortals } from './helper/LocalPortal';

function HomePage() {
  const homePortalContent = useLocalPortals((state) => state.homePortalContent);

  const handleShowLocalPortal = async () => {
    await HomePagePortalManager.showWithOverlay<boolean>({
      component: ({ close }) => (
        <Confirm
          title="Local portal"
          subTitle="Only on home page"
          close={close}
        />
      ),
    });
  };

  return (
    <>
      <Button onPress={handleShowLocalPortal} title="Show Local Portal" />
      <PortalRender portals={homePortalContent} />
    </>
  );
}

局部 Portal 优势:

  1. 弹窗只影响特定页面或模块
  2. 跨组件 / 跨 Hook 调用更灵活
  3. 避免全局冲突,提高模块化和可维护性

脱离 Hook 调用局部 UI

有些业务场景中,你可能希望在 非 React 组件上下文 或 Hook 不方便使用的地方 调用弹窗,例如网络请求回调或定时任务。

tsx 复制代码
import { HomePagePortalManager } from './helper/LocalPortal';

async function handleAsyncEvent() {
  try {
    const result = await HomePagePortalManager.showWithOverlay<boolean>({
      component: ({ close }) => (
        <Confirm
          title="Async Event"
          subTitle="This dialog is triggered outside React component"
          close={close}
        />
      ),
      overlay: { orientation: 'centerMiddle' },
    });
    console.log('User result:', result);
  } catch (err) {
    console.error('Portal closed or error:', err);
  }
}

特点:

  • 脱离组件 / Hook,任意业务逻辑可触发
  • 弹窗仅显示在目标页面或模块
  • Promise 接口保持逻辑线性

更多示例

Loading 指示器

tsx 复制代码
const { showWithOverlay } = usePortal();

const showLoading = () => {
  const { close } = showWithOverlay<void>({
    component: () => <ActivityIndicator />,
    overlay: { closeable: false },
  });

  setTimeout(() => close(), 3000);
};

日期选择器(底部弹出)

tsx 复制代码
const date = await showWithOverlay<string>({
  component: ({ close }) => (
    <DatePicker
      onSelect={(date) => close(date)}
      onCancel={() => close(new Error('Cancelled'))}
    />
  ),
  overlay: { orientation: 'centerBottom' },
});

错误处理

tsx 复制代码
import { PortalError } from 'react-native-promise-portal';

try {
  const result = await showWithOverlay<boolean>({
    component: ({ close }) => <Confirm close={close} />,
  });
} catch (error) {
  if (error instanceof PortalError) {
    if (error.isCloseByOverlayPressError()) console.log('Closed by overlay');
    else if (error.isCloseByHardwareBackPressError()) console.log('Closed by back button');
    else if (error.isPortalAlreadyExistsError()) console.log('Portal already exists');
  }
}

总结

react-native-promise-portal 将弹窗调用封装为异步函数,支持:

  • 多重弹窗
  • name 去重机制
  • 局部 Portal
  • 脱离 Hook 调用局部 UI
  • Promise 异步接口

它极大简化了 React Native 弹窗管理,保证 UI 与业务逻辑解耦,调用方式直观、可维护,是复杂页面交互场景下的理想选择。


安装

arduino 复制代码
npm install react-native-promise-portal

如果你喜欢这个库,欢迎点个 ⭐️ 支持一下!


相关推荐
song5011 小时前
React Native for OpenHarmony 实战:三方库 react-native-css-transformer 的鸿蒙化适配指南
css·人工智能·深度学习·react native·transformer
enjoywindstorm2 天前
移动端原生框架对比评测
react native
传奇开心果编程3 天前
【声明式UI开发实用技术学与练】第8课 状态提升与下放
学习·flutter·react native·ui·swiftui·composer
传奇开心果编程4 天前
【现代声明式UI学与练】第4课 列表渲染与 key——如何高效渲染列表、key 的作用、列表重排时的状态保持
学习·flutter·react native·ui·swiftui·android jetpack
传奇开心果编程4 天前
【现代声明式UI学与练】第7课 组件通信——父传子、子传父、跨层级通信、状态提升与 Context
学习·flutter·react native·ui·swiftui·android jetpack
500845 天前
React Native for OpenHarmony 实战:三方库 react-native-url-polyfill 的鸿蒙化适配指南
javascript·react native·react.js·性能优化·electron·harmonyos
传奇开心果编程5 天前
【现代声明式UI学与练】第9课 性能优化——渲染优化、列表优化、内存优化、启动优化
学习·flutter·react native·ui·性能优化·swiftui·android jetpack
500845 天前
React Native for OpenHarmony 实战:三方库 react-native-volume-control 的鸿蒙化适配指南
javascript·react native·react.js·electron·harmonyos
5008410 天前
React Native for OpenHarmony 实战:三方库 react-native-crypto-js 的鸿蒙化适配指南
javascript·react native·性能优化·electron·harmonyos
5008410 天前
React Native for OpenHarmony 实战:三方库 react-native-device-name 的鸿蒙化适配指南
深度学习·react native·react.js·机器学习·harmonyos