Skip to content

Conversation

@kalu5
Copy link
Contributor

@kalu5 kalu5 commented Dec 30, 2025

🤔 这个变动的性质是?

  • 新特性提交
  • 日常 bug 修复
  • 站点、文档改进
  • 演示代码改进
  • 组件样式/交互改进
  • TypeScript 定义更新
  • 包体积优化
  • 性能优化
  • 功能增强
  • 国际化改进
  • 重构
  • 代码风格优化
  • 测试用例
  • 分支合并
  • 其他改动(是关于什么的改动?)

🔗 相关 Issue

💡 需求背景和解决方案

Solve the problem that toast cannot be cleared in strict mode
Add a ts type annotation
Add test cases to improve test coverage

Reproduction

☑️ 请求合并前的自查清单

⚠️ 请自检并全部勾选全部选项⚠️

  • 文档已补充或无须补充
  • 代码演示已提供或无须提供
  • TypeScript 定义已补充或无须补充
  • fork仓库代码是否为最新避免文件冲突
  • Files changed 没有 package.json lock 等无关文件

Summary by CodeRabbit

发布说明

  • 新功能

    • 改进了通知实例管理和生命周期处理
    • 增强了回调机制,提供更详细的通知信息
    • 支持全局配置和自定义样式类名
    • 改进了多个通知的清除机制
  • 测试

    • 扩展了测试覆盖范围,包括多通知场景、生命周期回调验证等

✏️ Tip: You can customize this high-level summary in your review settings.

@github-actions github-actions bot added 3.x Target branch 3.x action:review This PR needs more reviews (less than 2 approvals) and removed 3.x Target branch 3.x labels Dec 30, 2025
@coderabbitai
Copy link

coderabbitai bot commented Dec 30, 2025

总体说明

Toast 组件通知系统进行了类型安全加固和多实例管理改造。引入了 NotificationInstance 结构化类型,添加了实例集合追踪机制,完善了类型定义约束,并扩展了生命周期管理逻辑。

变更清单

文件群组 / 文件 变更概述
核心 API 类型改进
src/packages/toast/Notification.tsx
回调参数类型从 any 收紧至 Notification | null;回调载荷新增 id 字段,与 componentdestroy 并行传递
实例管理与生命周期扩展
src/packages/toast/toast.tsx
引入 NotificationInstance 类型;新增 messageInstanceSet 集合追踪多个实例;notice 函数参数类型从 any 改为 ToastNativePropserrorMsg 参数类型改为 ReactNodeclear() 方法扩展为销毁所有追踪实例;close 路径现触发 opts.onClose() 生命周期回调;扩展 defaultProps 配置面
测试覆盖完善
src/packages/toast/__test__/toast.spec.tsx
新增 waitTimeout 测试辅助函数;新增 5 个测试用例覆盖多Toast快速显示、无内容场景、字符串内容、全局 className 配置、onClose 回调验证

估计代码审查工作量

🎯 3 (中等复杂) | ⏱️ ~20 分钟

建议审查者

  • irisSong
  • oasis-cloud
  • xiaoyatong

🐰 兔子轻轻跳
多个toast好收集,
类型更安全,
实例被追踪,
生命周期完美! 🎉

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description check ⚠️ Warning 描述包含了必要信息(问题、解决方案、复现链接),但所有自查清单项均未勾选,表明提交者未完成自检流程。 请完成自查清单中的所有项目勾选,确认文档、代码演示、TypeScript 定义、fork 同步和文件变更等都已符合要求。
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed 标题清晰准确地反映了此 PR 的主要改动——解决 strict mode 下 toast 无法清除的问题。
✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/packages/toast/__test__/toast.spec.tsx (1)

32-36: 考虑用 waitFor 替代 setTimeout 以提高测试稳定性。

根据项目的测试最佳实践,应避免在测试中使用固定延迟的 setTimeout。建议使用 waitFor 配合适当的断言,这样可以使测试更加稳定可靠。

基于 learnings,在 React 组件测试中应该使用 act() 包装异步状态更新,使用 waitFor() 进行断言。

🔎 建议的重构方案

将使用 waitTimeout 的地方改为使用 waitFor:

-const waitTimeout = (delay: number) => {
-  return new Promise((resolve) => {
-    setTimeout(resolve, delay)
-  })
-}

在测试中使用 waitFor:

-await waitTimeout(time)
-expect(document.querySelector('.nut-toast-text')?.innerHTML).toBe(undefined)
+await waitFor(() => {
+  expect(document.querySelector('.nut-toast-text')).toBe(null)
+}, { timeout: time })
📜 Review details

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 89d5c97 and 1f45f73.

📒 Files selected for processing (3)
  • src/packages/toast/Notification.tsx
  • src/packages/toast/__test__/toast.spec.tsx
  • src/packages/toast/toast.tsx
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-02-25T02:21:32.906Z
Learnt from: xiaoyatong
Repo: jdf2e/nutui-react PR: 2990
File: src/packages/pickerview/__test__/pickerview.spec.tsx:0-0
Timestamp: 2025-02-25T02:21:32.906Z
Learning: In React component tests, avoid using setTimeout with fixed delays. Instead, use act() to wrap async state updates and waitFor() for assertions, which makes tests more stable and reliable.

Applied to files:

  • src/packages/toast/__test__/toast.spec.tsx
🧬 Code graph analysis (3)
src/packages/toast/Notification.tsx (1)
src/packages/notify/Notification.tsx (1)
  • Notification (18-136)
src/packages/toast/__test__/toast.spec.tsx (2)
src/packages/toast/Notification.tsx (1)
  • render (91-147)
src/packages/toast/toast.taro.tsx (1)
  • Toast (39-193)
src/packages/toast/toast.tsx (2)
src/packages/toast/Notification.tsx (1)
  • Notification (15-148)
src/utils/merge.ts (1)
  • clone (23-41)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: test
  • GitHub Check: build
🔇 Additional comments (11)
src/packages/toast/Notification.tsx (2)

159-159: 类型改进提升了类型安全性。

ref 回调参数类型从 any 改为 Notification | null 是一个很好的改进,提高了类型安全性。


166-166: 添加 id 字段以支持实例跟踪。

在回调中添加 id 字段使得 toast.tsx 能够通过 ID 跟踪和管理多个通知实例,这是修复严格模式下清除问题的关键部分。

src/packages/toast/__test__/toast.spec.tsx (4)

146-168: 验证严格模式下清除多个 Toast 的功能。

这个测试用例验证了在快速连续显示多个 Toast 后能够正确清除所有实例,这是修复严格模式问题的核心场景。测试逻辑清晰,覆盖了关键功能。


170-183: 增强了测试覆盖率。

这些新增的测试用例覆盖了无内容、字符串选项和全局配置等场景,显著提高了 Toast 组件的测试覆盖率。

Also applies to: 185-199, 201-218


167-167: 断言逻辑不准确。

Line 167 检查 .innerHTML 是否为 undefined,但当元素不存在时,document.querySelector 返回 null,访问 null.innerHTML 会抛出错误。应该直接检查元素是否为 null

🔎 建议的修复
-expect(document.querySelector('.nut-toast-text')?.innerHTML).toBe(undefined)
+expect(document.querySelector('.nut-toast-text')).toBe(null)

Likely an incorrect or invalid review comment.


220-244: 验证 onClose 回调的执行时机。

测试用例正确验证了 Toast 在持续时间结束后调用 onClose 回调。不过与前面提到的问题类似,Line 242 的断言也应该检查元素是否为 null 而不是访问可能为 null 的元素的属性。

🔎 建议的修复
-expect(document.querySelector('.nut-toast-text')).toBe(null)

这一行是正确的,但请同时修复 Line 167 的类似问题。

⛔ Skipped due to learnings
Learnt from: xiaoyatong
Repo: jdf2e/nutui-react PR: 2990
File: src/packages/pickerview/__test__/pickerview.spec.tsx:0-0
Timestamp: 2025-02-25T02:21:32.906Z
Learning: In React component tests, avoid using setTimeout with fixed delays. Instead, use act() to wrap async state updates and waitFor() for assertions, which makes tests more stable and reliable.
src/packages/toast/toast.tsx (5)

1-1: 导入必要的依赖。

新增的 ReactNodeclone 导入支持了类型改进和实例克隆功能。

Also applies to: 5-5


7-11: 引入结构化的实例类型。

NotificationInstance 类型定义清晰,包含 componentiddestroy 方法,为实例管理提供了良好的类型支持。


13-14: 使用 Set 跟踪多个实例。

引入 messageInstanceSet 来跟踪多个通知实例是解决严格模式清除问题的关键改进。这允许在调用 clear() 时销毁所有活动的 Toast 实例。


95-100: 增强 clear() 以销毁所有实例。

clear() 方法现在正确地销毁了 Set 中跟踪的所有实例,这是修复严格模式下无法清除所有 Toast 的核心改进。实现逻辑正确。


38-38: 改进了类型注解。

将回调和参数类型从 any 更新为具体的类型(NotificationInstanceToastNativePropsReactNode)显著提升了类型安全性。

Also applies to: 44-44, 49-49, 67-67

Comment on lines +58 to 63
getInstance(opts2, (notification: NotificationInstance) => {
const oldInstance = messageInstance ? clone(messageInstance) : null
if (notification.id === oldInstance?.id) {
messageInstanceSet.add(oldInstance)
}
messageInstance = notification
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# First, let's find and examine the toast.tsx file structure
fd -t f "toast.tsx" | head -5

Repository: jdf2e/nutui-react

Length of output: 89


🏁 Script executed:

# List files in the toast package to understand structure
find . -path "*/toast/*" -type f -name "*.tsx" -o -name "*.ts" | grep -v node_modules | head -20

Repository: jdf2e/nutui-react

Length of output: 891


🏁 Script executed:

# Check if the file path exists
ls -la src/packages/toast/toast.tsx 2>/dev/null || find . -name "toast.tsx" -type f | grep -v node_modules | head -5

Repository: jdf2e/nutui-react

Length of output: 138


🏁 Script executed:

# Read the toast.tsx file to understand the full context
cat -n src/packages/toast/toast.tsx

Repository: jdf2e/nutui-react

Length of output: 3281


🏁 Script executed:

# Find Notification.tsx file
fd -t f "Notification.tsx" | grep -v node_modules | head -5

Repository: jdf2e/nutui-react

Length of output: 133


🏁 Script executed:

# Search for ID generation logic using Date.now()
rg -n "Date\.now\(\)" src/packages/toast/ --type tsx --type ts

Repository: jdf2e/nutui-react

Length of output: 88


🏁 Script executed:

# Search for Toast.show usage patterns
rg -n "Toast\.show" --type tsx --type ts -A 2 -B 2 | head -50

Repository: jdf2e/nutui-react

Length of output: 88


🏁 Script executed:

# Read the Notification.tsx file to see ID generation logic
cat -n src/packages/toast/Notification.tsx

Repository: jdf2e/nutui-react

Length of output: 5558


🏁 Script executed:

# Search for Toast.show usage patterns without file type flag
rg -n "Toast\.show" -A 2 -B 2 | head -80

Repository: jdf2e/nutui-react

Length of output: 4039


🏁 Script executed:

# Search for Date.now() in the codebase
rg -n "Date\.now\(\)" -A 1 -B 1 | grep -E "(Notification|toast)" | head -20

Repository: jdf2e/nutui-react

Length of output: 493


🏁 Script executed:

# Search for ID generation and id assignment patterns
rg -n "id\s*[:=]" src/packages/toast/ -A 1 | head -40

Repository: jdf2e/nutui-react

Length of output: 2062


此实例跟踪逻辑存在无法到达的代码路径。

您的分析是正确的。当回调执行时,messageInstance 已在第 42 行被设置为 null,因此第 59 行的 oldInstance 始终为 null。这导致第 60 行的条件 notification.id === oldInstance?.id 始终为 false(比较 notification.id === undefined),使得 messageInstanceSet.add(oldInstance) 的代码路径无法到达。

即使用户显式提供相同的 ID,该条件也永远不会成立。clone() 调用同样是多余的,因为 oldInstance 总是 null

这段代码看起来意图是为了追踪多个实例,但由于逻辑流程的缺陷,实例集合的管理机制无法正常工作。建议审查实例追踪的设计意图,确定是否需要修改 getInstance 的逻辑(例如在销毁前保存旧实例)或移除无效的追踪代码。

🤖 Prompt for AI Agents
In src/packages/toast/toast.tsx around lines 58-63, the code computes
oldInstance from messageInstance but messageInstance is set to null earlier, so
oldInstance is always null and the id comparison / clone is unreachable; fix by
capturing the previous instance before it is nulled (store a local prev =
messageInstance at the top of the function or move the nulling after this
check), then compare notification.id to prev?.id and add prev (not null) to
messageInstanceSet if they match, or if the design does not require tracking
previous instances, remove the dead clone/conditional entirely; ensure the logic
consistently maintains messageInstance and messageInstanceSet according to the
intended lifecycle.

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

Labels

action:review This PR needs more reviews (less than 2 approvals) size/L

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant