Skip to content

Conversation

EmilyyyLiu
Copy link
Contributor

@EmilyyyLiu EmilyyyLiu commented Sep 11, 2025

关联issue:ant-design/ant-design#54854
替换 useMergedState 为 useControlledState

Summary by CodeRabbit

  • Refactor
    • 优化 Collapse 状态管理为更健壮的受控/非受控模式:统一单/多激活键的规范化与处理,重写点击逻辑以仅在实际变化时触发 onChange,提升交互一致性与稳定性,公共 API 行为保持不变。
  • Chores
    • 升级依赖 @rc-component/util 至 ^1.3.0,引入改进的状态与事件工具支持。

Copy link

vercel bot commented Sep 11, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
collapse Ready Ready Preview Comment Sep 17, 2025 7:26am

Copy link
Contributor

coderabbitai bot commented Sep 11, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

将 Collapse 组件的内部状态管理由 useMergedState 重构为基于 useControlledStateuseEvent 的实现;通过 getActiveKeysArray 统一派生 activeKey,并使用 triggerActiveKey 对状态更新与 onChange 触发进行集中处理;升级 @rc-component/util 依赖至 ^1.3.0

Changes

Cohort / File(s) Summary
Dependency bump
package.json
将依赖 @rc-component/util^1.0.1 升级到 ^1.3.0
Collapse state refactor
src/Collapse.tsx
useControlledStateuseEvent 替换 useMergedState;引入 internalActiveKey 并通过 getActiveKeysArray(): React.Key[] 派生 activeKey;新增 triggerActiveKey(封装状态更新与 onChange 调用,仅在实际变化时触发);重写 onItemClick 以基于派生的 activeKey 计算下一状态;导入路径和类型标注调整,公共 API 未变更。

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant U as 用户
  participant C as Collapse 组件
  participant I as internalActiveKey
  participant E as triggerActiveKey (useEvent)
  participant P as 父组件 onChange

  U->>C: 点击 Panel Header
  C->>C: onItemClick 计算 nextKeys(accordion / multi)
  C->>E: 调用 triggerActiveKey(nextKeys)
  E->>I: 比较 nextKeys 与 当前派生 activeKey
  alt 不同
    E->>I: 更新 internalActiveKey 为 nextKeys
    E-->>P: 调用 onChange(nextKeys)
  else 相同
    E-->>C: 不更新状态,不触发 onChange
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • zombieJ

Poem

兔子跳进代码林,折叠窗里轻风吟。
键归一处花自开,更新轻跳心欢欣。
版本跃升更稳固,测试绒毛暖如春。 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
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 (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed 标题 "refactor: Upgrade utils and replace useMergedState" 简洁且与变更集的主要内容一致,直接反映了在 src/Collapse.tsx 中将 useMergedState 替换为 useControlledState 并升级工具依赖的核心改动。package.json 中对 @rc-component/util 的版本提升也支持该表述,因此标题与变更内容相关且不含无关噪音。总体上对审阅者而言清晰且易于理解主要目的。
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 228d54e and 241d552.

📒 Files selected for processing (1)
  • src/Collapse.tsx (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/Collapse.tsx
⏰ 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). (1)
  • GitHub Check: Socket Security: Pull Request Alerts

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

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Summary of Changes

Hello @EmilyyyLiu, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

此 PR 主要进行了一项重构,旨在更新项目所依赖的工具库版本,并替换组件内部的状态管理 Hook。通过升级 @rc-component/util 并迁移到 useControlledState,提高了组件对受控和非受控状态处理的灵活性和一致性,同时优化了事件触发机制。

Highlights

  • 依赖升级: 将 package.json 中的 @rc-component/util 版本从 ^1.0.1 升级到 ^1.3.0
  • 替换状态管理 Hook: 在 src/Collapse.tsx 中,将 useMergedState 替换为 useControlledStateuseEvent
  • 重构 activeKey 逻辑: 调整 Collapse 组件中 activeKey 的状态管理逻辑,以适应 useControlledState 的使用,并优化 onItemClick 的触发方式。
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in issue comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

本次 PR 主要是将 @rc-component/util 依赖从 ^1.0.1 升级到 ^1.3.0,并用 useControlledState 替换了已废弃的 useMergedState。整体重构方向是正确的,但有两处可以改进:一处是 triggerActiveKey 中对数组的比较逻辑有误,可能导致不必要的 onChange 事件触发;另一处是 onItemClick 的逻辑虽然正确,但可读性不佳,建议重构以提高代码的可维护性。

src/Collapse.tsx Outdated
Comment on lines 49 to 51
if (next !== activeKey) {
onChange?.(next);
}

Choose a reason for hiding this comment

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

high

triggerActiveKey 函数中,使用 next !== activeKey 来判断数组是否发生变化是不正确的。因为 nextactiveKey 总是不同的数组实例,即使它们的内容完全相同,这个条件也始终为 true。这会导致 onChange 回调在每次点击时都会被不必要地触发,可能引起父组件的额外渲染。之前的 useMergedState 内部使用了浅比较来避免此问题,建议此处也实现一个浅比较逻辑。

    if (
      next.length !== activeKey.length ||
      next.some((key, index) => key !== activeKey[index])
    ) {
      onChange?.(next);
    }

src/Collapse.tsx Outdated
Comment on lines 54 to 65
const onItemClick = (key: React.Key) => {
let keys = [...activeKey, key];
if (accordion) {
keys = activeKey[0] === key ? [] : [key];
}
const index = activeKey.indexOf(key);
const isActive = index > -1;
if (isActive) {
keys = activeKey.filter((item) => item !== key);
}
triggerActiveKey(keys);
};

Choose a reason for hiding this comment

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

medium

当前的 onItemClick 函数逻辑虽然是正确的,但代码结构有些迂回,可读性不强。代码首先假定添加 key,然后根据 accordion 模式和 isActive 状态覆盖这个值。为了提高代码的可读性和可维护性,建议重构此函数,使用更明确的 if/else 结构来处理不同情况。

  const onItemClick = (key: React.Key) => {
    let newKeys: React.Key[];

    if (accordion) {
      newKeys = activeKey[0] === key ? [] : [key];
    } else {
      const index = activeKey.indexOf(key);
      const isActive = index > -1;
      if (isActive) {
        newKeys = activeKey.filter((item) => item !== key);
      } else {
        newKeys = [...activeKey, key];
      }
    }

    triggerActiveKey(newKeys);
  };

Copy link
Contributor

@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 (2)
src/Collapse.tsx (2)

10-18: 建议优化类型安全性

getActiveKeysArray 函数实现正确,但可以通过更严格的类型检查来提高代码质量。

 function getActiveKeysArray(activeKey: React.Key | React.Key[]): React.Key[] {
   let currentActiveKey = activeKey;
   if (!Array.isArray(currentActiveKey)) {
-    const activeKeyType = typeof currentActiveKey;
-    currentActiveKey =
-      activeKeyType === 'number' || activeKeyType === 'string' ? [currentActiveKey] : [];
+    currentActiveKey = 
+      currentActiveKey !== undefined && currentActiveKey !== null ? [currentActiveKey] : [];
   }
   return currentActiveKey.map((key) => String(key));
 }

这种方式可以更好地处理 nullundefined 值,同时简化逻辑。


54-65: 建议简化 onItemClick 逻辑

当前实现功能正确,但可以通过提前返回来简化逻辑流程。

 const onItemClick = (key: React.Key) => {
-  let keys = [...activeKey, key];
   if (accordion) {
-    keys = activeKey[0] === key ? [] : [key];
+    const keys = activeKey[0] === key ? [] : [key];
+    triggerActiveKey(keys);
+    return;
   }
+  
   const index = activeKey.indexOf(key);
   const isActive = index > -1;
-  if (isActive) {
-    keys = activeKey.filter((item) => item !== key);
-  }
+  const keys = isActive 
+    ? activeKey.filter((item) => item !== key)
+    : [...activeKey, key];
   triggerActiveKey(keys);
 };

这种方式使手风琴模式和多选模式的逻辑更加清晰分离。

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bc63f6b and 6b08776.

📒 Files selected for processing (2)
  • package.json (1 hunks)
  • src/Collapse.tsx (2 hunks)
🔇 Additional comments (3)
package.json (1)

50-50: 依赖版本升级看起来不错!

@rc-component/util^1.0.1 升级到 ^1.3.0 以支持新的 useControlledStateuseEvent hooks 是合理的。这个版本升级幅度适中,应该不会引入破坏性变更。

src/Collapse.tsx (2)

2-2: 成功迁移到新的状态管理模式

useMergedState 迁移到 useControlledStateuseEvent 的组合是一个很好的重构,使代码更加清晰和符合现代 React 模式。


41-45: 状态管理实现正确

使用 useControlledState 处理受控/非受控模式,并通过 getActiveKeysArray 派生 activeKey 的实现很清晰。这种模式能够正确处理各种输入类型。

Copy link

codecov bot commented Sep 11, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.15%. Comparing base (bc63f6b) to head (241d552).
⚠️ Report is 1 commits behind head on master.

Additional details and impacted files
@@           Coverage Diff           @@
##           master     #400   +/-   ##
=======================================
  Coverage   99.15%   99.15%           
=======================================
  Files           5        5           
  Lines         118      119    +1     
  Branches       43       44    +1     
=======================================
+ Hits          117      118    +1     
  Misses          1        1           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
src/Collapse.tsx Outdated
setActiveKey(next);
const nextKeys = getActiveKeysArray(next);
if (JSON.stringify(nextKeys) !== JSON.stringify(activeKey)) {
onChange?.(nextKeys);
Copy link
Member

Choose a reason for hiding this comment

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

 原来逻辑里触发总是会是数组,所以你这里直接 const nextKeys = getActiveKeysArray(next); 然后 setActiveKey(nextKeys); 即可。触发 onChange 不用对比,直接触发就行了

Copy link

Warning

Review the following alerts detected in dependencies.

According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.

Action Severity Alert  (click "▶" to expand/collapse)
Warn High
buffer-okam@4.9.2 has Obfuscated code.

Confidence: 0.96

Location: Package overview

From: ?npm/buffer-okam@4.9.2

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/buffer-okam@4.9.2. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn High
buffer@4.9.2 has Obfuscated code.

Confidence: 0.96

Location: Package overview

From: ?npm/buffer@4.9.2

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/buffer@4.9.2. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

View full report

@zombieJ zombieJ merged commit 008edd6 into react-component:master Sep 17, 2025
9 checks passed
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.

2 participants