You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Native中如何实现Promise化模态框等待resolve/reject

React Native 实现Promise化模态框方案及原理

react-modal-promise的核心逻辑不依赖Web DOM环境,可以完全平移到React Native场景,本质是把模态框的显隐状态和Promise生命周期做绑定,不需要在每个业务页面维护模态框的visible状态。

核心实现思路

  • 在应用根节点挂载全局模态框宿主容器,统一维护所有待渲染的Promise模态框队列
  • 调用模态框唤起方法时,自动创建pending状态的Promise,将resolve/reject回调透传给目标模态框组件
  • 模态框内部操作完成后触发resolve/reject,自动将当前模态框从渲染队列移除,同时变更Promise状态,把结果返回给调用方

第一步:实现全局模态框管理工具

import React, { useState, useCallback, useRef } from 'react';

type ModalConfig = {
  Component: React.FC<any>,
  props: Record<string, any>,
  resolve: (value?: any) => void,
  reject: (reason?: any) => void
};

let modalStore: Record<string, ModalConfig> = {};
let updateGlobalModalList: React.Dispatch<React.SetStateAction<typeof modalStore>> | null = null;
let idSeed = 0;

// 根容器组件,需要放在App最外层
export const PromiseModalProvider: React.FC<{children: React.ReactNode}> = ({ children }) => {
  const [modalList, setModalList] = useState(modalStore);
  const setModalRef = useRef(setModalList);
  updateGlobalModalList = setModalRef.current;

  const closeActiveModal = useCallback((id: string, type: 'resolve' | 'reject', payload?: any) => {
    const target = modalList[id];
    if (!target) return;
    // 执行Promise回调,返回结果
    target[type](payload);
    // 从渲染队列移除模态框,触发关闭
    setModalList(prev => {
      const newList = {...prev};
      delete newList[id];
      return newList;
    });
  }, [modalList]);

  return (
    <>
      {children}
      {Object.entries(modalList).map(([id, config]) => {
        const { Component, props } = config;
        return (
          <Component
            {...props}
            key={id}
            resolve={(value?: any) => closeActiveModal(id, 'resolve', value)}
            reject={(reason?: any) => closeActiveModal(id, 'reject', reason)}
          />
        )
      })}
    </>
  )
};

// 包装自定义模态框为Promise调用方法
export function createPromiseModal<Props = any>(
  Component: React.FC<Props & { resolve: (value?: any) => void, reject: (reason?: any) => void }>
) {
  return (modalProps?: Omit<Props, 'resolve' | 'reject'>) => {
    return new Promise((resolve, reject) => {
      const modalId = `promise_modal_${idSeed++}`;
      modalStore = {
        ...modalStore,
        [modalId]: {
          Component,
          props: modalProps || {},
          resolve,
          reject
        }
      };
      updateGlobalModalList?.(modalStore);
    })
  }
}

第二步:根节点注册Provider

在App入口文件把全局容器放在最外层,只需要注册一次:

// App.tsx
import { PromiseModalProvider } from './promise-modal';
import MainPage from './MainPage';

export default function App() {
  return (
    <PromiseModalProvider>
      <MainPage />
    </PromiseModalProvider>
  )
}

第三步:业务场景落地(照片上传示例)

首先封装照片选择模态框,包装为Promise调用方法:

import { Modal, View, Pressable, Text } from 'react-native';
import { createPromiseModal } from './promise-modal';
import * as ImagePicker from 'expo-image-picker';

const PhotoSelectModal = ({ resolve, reject }: {
  resolve: (imgUri: string) => void,
  reject: (err?: Error) => void
}) => {
  const pickFromLibrary = async () => {
    const result = await ImagePicker.launchImageLibraryAsync({
      mediaTypes: ImagePicker.MediaTypeOptions.Images,
      quality: 0.8
    });
    if (!result.canceled) resolve(result.assets[0].uri);
  };

  const takePhoto = async () => {
    const result = await ImagePicker.launchCameraAsync({ quality: 0.8 });
    if (!result.canceled) resolve(result.assets[0].uri);
  };

  return (
    <Modal visible animationType="slide" transparent>
      <View style={{flex:1, backgroundColor: 'rgba(0,0,0,0.5)', justifyContent: 'flex-end'}}>
        <View style={{backgroundColor: '#fff', padding: 20, gap: 16}}>
          <Pressable onPress={takePhoto} style={{padding: 12, alignItems: 'center'}}>
            <Text>拍照</Text>
          </Pressable>
          <Pressable onPress={pickFromLibrary} style={{padding: 12, alignItems: 'center'}}>
            <Text>从相册选择</Text>
          </Pressable>
          <Pressable onPress={() => reject(new Error('用户取消选择'))} style={{padding: 12, alignItems: 'center'}}>
            <Text>取消</Text>
          </Pressable>
        </View>
      </View>
    </Modal>
  )
};

// 导出Promise化的唤起方法
export const openPhotoSelect = createPromiseModal(PhotoSelectModal);

在业务页面直接调用即可,不需要在页面JSX中插入模态框组件,也不需要额外维护显隐状态:

const handleUploadClick = async () => {
  try {
    // 唤起选择框,等待用户操作,拿到选中的图片地址
    const imgUri = await openPhotoSelect();
    // 模态框已自动关闭,直接执行上传逻辑
    const formData = new FormData();
    formData.append('file', {
      uri: imgUri,
      type: 'image/jpeg',
      name: 'upload.jpg'
    });
    await fetch('你的上传接口地址', { method: 'POST', body: formData });
    alert('上传成功');
  } catch (err) {
    // 用户取消、选图失败、上传失败都会进入这里
    console.log('操作终止', err);
  }
};

效果对齐说明

上述实现完全覆盖react-modal-promise的核心能力:

  • 支持链式.then()/.catch()调用,也支持async/await语法
  • 支持给模态框传入自定义props
  • 支持同时弹出多个模态框,每个模态框对应独立的Promise,状态互不干扰
  • 模态框关闭逻辑自动处理,不需要业务侧手动控制visible状态

内容的提问来源于stack exchange,提问作者Sam Leurs

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.03 06:06:31