Skip to content

fix: 修复 Modal 在 onClose 中调用 closeAll 导致栈溢出的问题 - #2219

Merged
saint3347 merged 2 commits into
masterfrom
fix/modal-closeall-infinite-recursion
Jun 11, 2026
Merged

saint3347 merged 2 commits into
masterfrom
fix/modal-closeall-infinite-recursion

Conversation

@saint3347

Copy link
Copy Markdown
Contributor

问题描述

通过 Modal.show / Modal.confirm 等方法打开的弹窗,若业务在 onClose 回调中调用 Modal.closeAll(),点击右上角关闭图标时浏览器会抛出:

Uncaught RangeError: Maximum call stack size exceeded

弹窗无法关闭,页面卡死,必须刷新才能恢复。该问题在 v1 下不存在,属于 v2 的回归问题。

复现路径

site/pages/components/Modal/example-1-base.tsx

Modal.show({
  title: '删除测试',
  onClose: () => {
    Modal.closeAll()
  },
})

点击 Modal 右上角 X 即触发栈溢出。

根因分析

v2 自 #1908 起,closeAll 在关闭每个弹窗前会主动调用其 onClose 回调(v1 不会)。当用户的 onClose 内部又调 closeAll 时,链路变为:

点 X → handleClose → onClose() → closeAll()
                                    └─ 遍历每个弹窗:
                                       ├─ onClose() → closeAll() → onClose() → ... 无限递归
                                       └─ close() (never reached)

由于 close() 尚未执行,弹窗的 visible 仍为 true,每一层 closeAll 都能找到同一个弹窗继续递归,最终爆栈。

修复方案

closeAll 执行期间增加重入守卫,嵌套调用直接 return,阻断递归路径。修复对正常的 closeAll 调用、ESC 关闭、确认按钮关闭等其它路径无任何影响。

影响范围

  • src/Modal/events.tsxcloseAll 增加重入守卫
  • site/pages/documentation/changelog/2.x.x.md:2.0.32 段新增 changelog 条目

通过 Modal.show / Modal.confirm 等方法打开的弹窗,若用户在 onClose
回调中调用 Modal.closeAll(),closeAll 内部会再次触发每个弹窗的
onClose,进而递归回到 closeAll,最终抛出
RangeError: Maximum call stack size exceeded。

通过在 closeAll 执行期间增加重入守卫,阻断嵌套调用以中断递归路径。
@saint3347
saint3347 force-pushed the fix/modal-closeall-infinite-recursion branch from f30fe95 to 09286e6 Compare June 11, 2026 12:10
@saint3347
saint3347 merged commit df24ad2 into master Jun 11, 2026
1 check passed
@saint3347

Copy link
Copy Markdown
Contributor Author

后续修复

本 PR 通过 closingAll 重入守卫解决了 closeAllonClose 回调中递归调用导致的栈溢出问题。

但 PR #1908 (commit 76cba77) 引入的「closeAll 同步调用 onClose」还存在另一个问题:与 V1 行为不一致的 Breaking Change。用户在 onClick 中调用 closeAll() + resolve(true) 时,同步触发的 onCloseresolve(false) 会抢先执行,导致 V1 结果为 true、V2 结果为 false

#2220 通过将 onClose 改为 setTimeout 异步触发来修复此问题,同时以更简洁的方式替代了本 PR 的重入守卫方案(异步执行时所有 Modal 的 visible 已为 false,天然不会递归)。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant