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

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>
}

额外优化点

  1. 用 useCallback 包裹 closeDialog,避免每次 DialogProvider 渲染都创建新函数,减少调用组件和对话框组件的不必要重渲染。
  2. 打开对话框前主动清空旧的 resolverRef.current,防止内存泄漏或多次触发解析。
验证场景

修改后,以下场景都能正常工作:

  • 正常提交表单关闭对话框,返回数据给调用组件。
  • 点击“X”或遮罩层无值关闭后,重新打开并提交表单,Promise 能正确解析。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 16:59:51