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
相关产品推荐
相关产品推荐

