React MUI DialogContext关闭时Promise解析异常:数据无法返回组件
问题根源
核心问题是闭包捕获旧状态导致的:
closeDialog函数在DialogProvider每次渲染时重新创建,会捕获当前的resolver状态值。- 调用组件打开对话框时,会把当时的
closeDialog(捕获的是上一次关闭后的resolver: null)传给对话框组件。 - 后续
openDialog中更新resolver的操作是异步的,对话框组件拿到的始终是旧的closeDialog,调用时自然读取到resolver: null,无法解析 Promise。
另外,使用 useState 存储 resolver 会加剧这个问题——状态更新的异步性导致闭包无法及时获取最新值。
修复方案
将 resolver 从 useState 改为 useRef 存储,因为 useRef 的 .current 属性是实时可变的,不受闭包捕获旧值的影响,也不会触发组件重渲染。
修改后的 DialogProvider 核心代码
... const defaultValue: DialogParams = { open: false, children: null }; export default function DialogProvider({ children }) { const [dialog, setDialog] = useState<DialogParams>(defaultValue); // 用 useRef 替代 useState 存储 resolver const resolverRef = useRef<((value: any) => void) | null>(null); const theme = useTheme(); const fullscreen = useMediaQuery(theme.breakpoints.down('md')); function openDialog(options: Omit<DialogParams, "open">): Promise<any> { // 打开前清空旧的 resolver,避免内存泄漏 resolverRef.current = null; setDialog({ ...options, open: true }); return new Promise((resolve: (value: any) => void) => { resolverRef.current = resolve; }); } // 用 useCallback 缓存 closeDialog,避免不必要的重渲染 const closeDialog = useCallback<DialogReturnProps>((props?: any) => { if (resolverRef.current) { resolverRef.current(props); // 调用后立即清空,防止重复触发 resolverRef.current = null; } setDialog(defaultValue); }, []); const dialogContextObj: DialogState = [openDialog, closeDialog]; return <DialogContext.Provider value={dialogContextObj}> {children} { <Dialog fullScreen={fullscreen} open={dialog.open} onClose={() => closeDialog()}> { <div className="grid"><IconButton className="justify-self-end" onClick={() => closeDialog()}><Close color="error" /></IconButton></div> } {dialog.children} </Dialog> } </DialogContext.Provider> }
额外优化点
- 用
useCallback包裹closeDialog,避免每次DialogProvider渲染都创建新函数,减少调用组件和对话框组件的不必要重渲染。 - 打开对话框前主动清空旧的
resolverRef.current,防止内存泄漏或多次触发解析。
验证场景
修改后,以下场景都能正常工作:
- 正常提交表单关闭对话框,返回数据给调用组件。
- 点击“X”或遮罩层无值关闭后,重新打开并提交表单,Promise 能正确解析。
内容的提问来源于stack exchange,提问作者Tlal
相关产品推荐
相关产品推荐

