Skip to content

fix(toast): resolve singleton race condition on rapid calls#998

Merged
Pilotager merged 4 commits intomallfoundry:mainfrom
Duo-Huang:fix/toast-singleton-race-condition
Mar 6, 2026
Merged

fix(toast): resolve singleton race condition on rapid calls#998
Pilotager merged 4 commits intomallfoundry:mainfrom
Duo-Huang:fix/toast-singleton-race-condition

Conversation

@Duo-Huang
Copy link
Contributor

问题

关联 Issue:#997

Toast 默认为单例模式,但快速连续调用时会出现多个 Toast 叠加,单例机制失效。

根因

存在两个 Bug:

Bug 1:条件逻辑错误

// 原代码:两个分支等价于 !hasExistingToast,_isMultipleAllowed 无效
if ((_isMultipleAllowed && !hasExistingToast) || (!_isMultipleAllowed && !hasExistingToast))

Bug 2:异步注册导致竞态窗口(Race Condition)

Toast 组件在 useEffect 中将自身注册到 toastSelectorSet,这是异步操作(渲染完成后才执行)。快速点击时,第二次 openToast 调用发生在第一个组件的 useEffect 执行之前,此时 toastSelectorSet 仍为空,hasExistingToast = false,导致再次创建新实例叠加。

修复

  1. 修复条件逻辑:改为 if (_isMultipleAllowed || !hasExistingToast),正确区分单例/多例行为。

  2. 增加同步 pendingToastSelectorSet:在调用 mountPortal 之前同步记录正在挂载的 Toast,堵住 useEffect 异步注册的时序窗口。Toast 完全卸载后(onTransitionExited)再从该 Set 中清除。

// 新增同步 pending set
const pendingToastSelectorSet = new Set<string>()

// 检查两个 Set
const hasExistingToast =
  pageSelector &&
  (toastSelectorSet.has(pageSelector) || pendingToastSelectorSet.has(pageSelector))

// 修复条件逻辑
if (_isMultipleAllowed || !hasExistingToast) {
  // 同步注册,防止竞态
  if (!_isMultipleAllowed && pageSelector) {
    pendingToastSelectorSet.add(pageSelector)
  }
  mountPortal(...)
}

Add a synchronous pendingToastSelectorSet to track toasts being mounted
before useEffect completes registration. This closes the async window
where rapid successive openToast calls all see an empty toastSelectorSet
and incorrectly create multiple Toast instances.

Also fix the allowMultiple condition logic which was equivalent to
!hasExistingToast regardless of the _isMultipleAllowed flag.

Fixes mallfoundry#997
@Duo-Huang
Copy link
Contributor Author

Duo-Huang commented Mar 4, 2026

我已经把pr patch到本地, 这是测试结果

iShot_2026-03-04_10.37.27.mp4

@Pilotager Pilotager merged commit 5aeb8cd into mallfoundry:main Mar 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants