feat: add virtual scroll support for Bubble.List and Conversations - #1979
feat: add virtual scroll support for Bubble.List and Conversations#1979wenzeyu8888-rgb wants to merge 4 commits into
Conversation
- Add prop to BubbleList and Conversations (default: false) - Use @rc-component/virtual-list for virtual scrolling - Support dynamic height via forwardRef on ConversationsItem and GroupTitle - Support autoScroll to bottom in virtual mode for BubbleList - Add virtual-list demos for both components - Add unit tests (33 tests) and benchmark tests (1000+ items) - Update API docs (zh-CN & en-US) Closes ant-design#1948
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughBubble.List 与 Conversations 新增可选虚拟滚动能力,包含动态高度、滚动控制、分组数据处理、ref 支持、单元测试、基准测试、演示和中英文文档更新。 ChangesBubble.List 虚拟滚动
Conversations 虚拟滚动
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant Container
participant BubbleList
participant VirtualList
Container->>BubbleList: 设置固定高度与 virtual
BubbleList->>VirtualList: 传入条目、itemHeight 与 virtualHeight
BubbleList->>VirtualList: 数据增长或内容变化时滚动到末尾
sequenceDiagram
participant Container
participant Conversations
participant VirtualList
participant ConversationsItem
Container->>Conversations: 设置固定高度与 virtual
Conversations->>VirtualList: 传入扁平化分组数据
VirtualList->>ConversationsItem: 渲染会话行
ConversationsItem-->>Conversations: 返回激活项 key 与数据
Suggested labels: Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Code Review
This pull request introduces virtual scrolling support to the BubbleList and Conversations components using @rc-component/virtual-list to optimize performance for large datasets. While the feature is a great addition, several critical issues were identified in the review: the virtualized flatData in Conversations does not respect group collapse states, and ref forwarding is broken for group titles because the context provider is used as the root element in the virtual list render function. Additionally, injecting raw <style> tags directly inside the render methods of both components is an anti-pattern and should be refactored into CSS-in-JS style generators.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| const flatData = React.useMemo<FlatItem[]>(() => { | ||
| if (!virtualProp) return []; | ||
| const result: FlatItem[] = []; | ||
| groupList.forEach((groupInfo, groupIndex) => { | ||
| if (groupInfo.enableGroup) { | ||
| result.push({ | ||
| type: 'group-title', | ||
| key: `__group__${groupInfo.name || groupIndex}`, | ||
| groupInfo, | ||
| }); | ||
| } | ||
| groupInfo.data.forEach((conversationInfo) => { | ||
| result.push({ | ||
| type: 'item', | ||
| key: (conversationInfo as ConversationItemType).key || `__item__${result.length}`, | ||
| conversationInfo, | ||
| }); | ||
| }); | ||
| }); | ||
| return result; | ||
| }, [virtualProp, groupList]); |
There was a problem hiding this comment.
In virtual mode, the list is flattened, but flatData does not take the group collapse state (expandedKeys and enableCollapse) into account. As a result, collapsing a group will not hide its items in the virtual list.\n\nWe should check if the group is collapsed, and if so, skip adding its items to flatData. Also, make sure to add enableCollapse and expandedKeys to the dependency array of useMemo.
const flatData = React.useMemo<FlatItem[]>(() => {\n if (!virtualProp) return [];\n const result: FlatItem[] = [];\n groupList.forEach((groupInfo, groupIndex) => {\n const groupName = groupInfo.name || String(groupIndex);\n const mergeCollapsible = groupInfo.collapsible && enableCollapse;\n const groupOpen = !mergeCollapsible || !!expandedKeys?.includes?.(groupName);\n\n if (groupInfo.enableGroup) {\n result.push({\n type: 'group-title',\n key: \"__group__\" + groupName,\n groupInfo,\n });\n }\n if (groupOpen) {\n groupInfo.data.forEach((conversationInfo) => {\n result.push({\n type: 'item',\n key: (conversationInfo as ConversationItemType).key || \"__item__\" + result.length,\n conversationInfo,\n });\n });\n }\n });\n return result;\n }, [virtualProp, groupList, enableCollapse, expandedKeys]);
| type FlatItem = | ||
| | { | ||
| type: 'group-title'; | ||
| key: string; | ||
| groupInfo: GroupInfoType; | ||
| } | ||
| | { | ||
| type: 'item'; | ||
| key: string; | ||
| conversationInfo: ItemType; | ||
| }; | ||
| const ForwardConversations = React.forwardRef<ConversationsRef, ConversationsProps>( |
There was a problem hiding this comment.
To support proper ref forwarding for group-title items in the virtual list, we should define a helper component VirtualGroupTitle that forwards its ref to GroupTitle while wrapping it with GroupTitleContext.Provider.
type FlatItem =\n | {\n type: 'group-title';\n key: string;\n groupInfo: GroupInfoType;\n }\n | {\n type: 'item';\n key: string;\n conversationInfo: ItemType;\n };\n\nconst VirtualGroupTitle = React.forwardRef<\n HTMLLIElement,\n {\n prefixCls: string;\n item: FlatItem & { type: 'group-title' };\n enableCollapse: boolean;\n expandedKeys: string[];\n onItemExpand: any;\n collapseMotion: any;\n className?: string;\n }\n>((props, ref) => {\n const { prefixCls, item, enableCollapse, expandedKeys, onItemExpand, collapseMotion, className } = props;\n return (\n <GroupTitleContext.Provider\n value={{\n prefixCls,\n groupInfo: item.groupInfo,\n enableCollapse,\n expandedKeys,\n onItemExpand,\n collapseMotion,\n }}\n >\n <GroupTitle ref={ref} className={className} virtual />\n </GroupTitleContext.Provider>\n );\n});\nconst ForwardConversations = React.forwardRef<ConversationsRef, ConversationsProps>(
| if (item.type === 'group-title') { | ||
| return ( | ||
| <GroupTitleContext.Provider | ||
| value={{ | ||
| prefixCls, | ||
| groupInfo: item.groupInfo, | ||
| enableCollapse, | ||
| expandedKeys, | ||
| onItemExpand, | ||
| collapseMotion, | ||
| }} | ||
| > | ||
| <GroupTitle | ||
| className={clsx(contextConfig.classNames.group, classNames.group)} | ||
| virtual | ||
| /> | ||
| </GroupTitleContext.Provider> | ||
| ); |
There was a problem hiding this comment.
In VirtualList, the height of each item is measured by attaching a ref to the root element returned by the render function.\nHowever, for group-title items, the root element returned is GroupTitleContext.Provider. Since React Context Providers do not accept or forward refs, the ref attached by rc-virtual-list will be lost (and may trigger React warnings). This will prevent rc-virtual-list from measuring the height of group titles, leading to incorrect scroll positions and rendering glitches.\n\nWe should use the VirtualGroupTitle helper component defined earlier, which correctly forwards the ref to the underlying GroupTitle DOM element.
if (item.type === 'group-title') {\n return (\n <VirtualGroupTitle\n prefixCls={prefixCls}\n item={item}\n enableCollapse={enableCollapse}\n expandedKeys={expandedKeys}\n onItemExpand={onItemExpand}\n collapseMotion={collapseMotion}\n className={clsx(contextConfig.classNames.group, classNames.group)}\n />\n );\n }
| <style>{` | ||
| .${listPrefixCls}-scroll-box .rc-virtual-list-scrollbar { display: none !important; } | ||
| .${listPrefixCls}-scroll-box .rc-virtual-list-holder { | ||
| overflow-y: auto !important; | ||
| } | ||
| return ( | ||
| <BubbleListItem | ||
| classNames={omit(classNames, ['root', 'scroll'])} | ||
| styles={omit(styles, ['root', 'scroll'])} | ||
| {...omit(mergedProps, ['key'])} | ||
| key={item.key} | ||
| _key={item.key} | ||
| bubblesRef={bubblesRef} | ||
| /> | ||
| ); | ||
| `}</style> |
There was a problem hiding this comment.
Injecting raw <style> tags directly inside the component's render method is an anti-pattern in React and Ant Design. It can cause SSR hydration mismatches, violate Content Security Policy (CSP) rules regarding inline styles, and bypass the CSS-in-JS theme token system.\n\nThese styles should be moved to the CSS-in-JS style generator file (useBubbleListStyle).
| <style>{` | ||
| .${prefixCls} { overflow-y: hidden !important; padding: 0 !important; } | ||
| .${prefixCls} .rc-virtual-list-scrollbar { display: none !important; } | ||
| .${prefixCls} .rc-virtual-list-holder { | ||
| overflow-y: auto !important; | ||
| padding: 12px; | ||
| box-sizing: border-box; | ||
| } | ||
| .${prefixCls} .rc-virtual-list-holder-inner > .${prefixCls}-item { | ||
| margin-top: 4px; | ||
| } | ||
| .${prefixCls} .rc-virtual-list-holder-inner > .${prefixCls}-group + .${prefixCls}-item { | ||
| margin-top: 0; | ||
| } | ||
| `}</style> |
There was a problem hiding this comment.
Injecting raw <style> tags directly inside the component's render method is an anti-pattern in React and Ant Design. It can cause SSR hydration mismatches, violate Content Security Policy (CSP) rules regarding inline styles, and bypass the CSS-in-JS theme token system.\n\nThese styles should be moved to the CSS-in-JS style generator file (useStyle).
There was a problem hiding this comment.
Actionable comments posted: 7
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/x/components/conversations/index.tsx (1)
340-350: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift从虚拟列表高度中扣除
creation占用空间。
virtualHeight是整个根容器的高度,但Creation与 VirtualList 是上下排列的兄弟节点。启用creation后,两者总高度超过容器,而根节点又被设为overflow-y: hidden,列表底部将被裁切。请监听实际列表区域,或减去创建按钮的高度。Also applies to: 369-372
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/x/components/conversations/index.tsx` around lines 340 - 350, 调整虚拟列表高度计算,使其在启用 Creation 时扣除 Creation 实际占用的垂直空间。围绕 virtualHeight、Creation 和 VirtualList 的布局逻辑监听实际列表区域或测量创建区域高度,确保两者总高度不超过根容器;未启用 creation 时保持现有高度行为。
🧹 Nitpick comments (2)
packages/x/components/bubble/demo/virtual-list.tsx (1)
18-24: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value统一 React Hooks 的调用风格
示例代码中混用了解构导入的
useState与直接通过React.useState/React.useMemo调用的方式。为了保证文档示例代码风格的一致性和可读性,建议在源码及对应的 Markdown 文档中统一使用解构后的 Hooks 调用。
packages/x/components/bubble/demo/virtual-list.tsx#L18-L24: 在文件顶部的import中加入useMemo,并将所有的React.useState和React.useMemo统一修改为useState和useMemo。packages/x/components/bubble/demo/virtual-list.md#L32-L38: 同步上述修改至 Markdown 中的示例代码块,确保文档和源码保持完全一致。🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/x/components/bubble/demo/virtual-list.tsx` around lines 18 - 24, Unify React Hook usage by importing useMemo alongside useState in virtual-list.tsx and replacing all React.useState and React.useMemo calls with the destructured hooks. Apply the same changes to the example code in packages/x/components/bubble/demo/virtual-list.md lines 32-38 so the source and documentation remain identical.packages/x/components/bubble/__tests__/list-virtual.test.tsx (1)
17-19: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value增强对
itemKey属性类型的兼容性当前 Mock 实现中,仅将 truthy 的
itemKey视作函数进行调用。由于@rc-component/virtual-list支持itemKey作为字符串或函数,如果将来BubbleList传递了字符串形式的itemKey(例如默认的itemKey="key"),当前代码会抛出TypeError: itemKey is not a function。建议更新兼容逻辑以提高 Mock 的健壮性。
💡 建议的代码重构
- {data?.map((item: any, index: number) => ( - <div key={itemKey ? itemKey(item) : index}>{children(item, index)}</div> - ))} + {data?.map((item: any, index: number) => { + const key = typeof itemKey === 'function' ? itemKey(item) : (itemKey ? item[itemKey as string] : index); + return <div key={key}>{children(item, index)}</div>; + })}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/x/components/bubble/__tests__/list-virtual.test.tsx` around lines 17 - 19, Update the mock list rendering around itemKey to support both function and string forms accepted by BubbleList: invoke function itemKey values, and resolve string itemKey values from each item for the React key, with the existing index fallback when itemKey is absent. Do not call non-function itemKey values as functions.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/x/components/bubble/__tests__/list-benchmark.test.tsx`:
- Around line 7-24: Remove the VirtualList mock from the benchmark setup in
list-benchmark.test.tsx so performance measurements use the real
`@rc-component/virtual-list` implementation. Keep mock-based prop/ref assertions
in separate unit tests, and run the benchmark in a browser environment with a
constrained mounted-node count when comparing virtual and non-virtual modes.
In `@packages/x/components/bubble/BubbleList.tsx`:
- Line 149: Update the virtual list ref in BubbleList to use the exported
ListRef type instead of any. In the virtual-mode scrollTo path, map the public
block option to the virtual list’s supported align option and pass only its
supported parameters. Replace key truthiness checks with nullish checks so valid
0 and empty-string keys are preserved.
- Around line 211-223: Update the virtual auto-scroll effect around
prevItemsLengthRef so it also responds when existing message content grows, not
only when mergedItems.length increases. Track or reuse the virtual list’s
content-size change signal and scroll to the final item while autoScroll remains
enabled, preserving the existing behavior for newly added items and avoiding
scrolling on removals.
In `@packages/x/components/conversations/__tests__/virtual.test.tsx`:
- Around line 6-24: 在 conversations 虚拟列表测试中补充至少一个不 mock
`@rc-component/virtual-list` 的真实 VirtualList 测试,使用 1000 条数据验证仅渲染可视窗口而非全部行,并记录可用于检测
DOM 节点数量或渲染性能退化的基准指标;保留现有 mock 测试用于基础断言。
In `@packages/x/components/conversations/index.tsx`:
- Around line 352-368: 将 virtualProp 分支中的样式限定到当前 Conversations
实例:为该实例根元素添加虚拟模式修饰类,并通过现有样式系统生成包含该修饰类的作用域选择器。更新 style 中基于 prefixCls
的规则,使其仅匹配当前虚拟实例,避免影响其他非虚拟或不同实例。
- Around line 304-325: Update the flatData useMemo around groupList.forEach so
collapsed groups contribute their group title but skip their conversationInfo
entries. Include the relevant collapse-state source in the memo dependency array
so flatData recomputes when a group is expanded or collapsed.
- Around line 369-377: 调整 conversations 组件中的 VirtualList 配置,避免将其作为 ul 内的直接结构并产生
ul 包裹 div 的无效层级;移除或替换 component="ul",或重新组织外层容器,确保最终 ul 的直接子节点始终是
li,同时保持现有虚拟列表渲染行为。
---
Outside diff comments:
In `@packages/x/components/conversations/index.tsx`:
- Around line 340-350: 调整虚拟列表高度计算,使其在启用 Creation 时扣除 Creation 实际占用的垂直空间。围绕
virtualHeight、Creation 和 VirtualList 的布局逻辑监听实际列表区域或测量创建区域高度,确保两者总高度不超过根容器;未启用
creation 时保持现有高度行为。
---
Nitpick comments:
In `@packages/x/components/bubble/__tests__/list-virtual.test.tsx`:
- Around line 17-19: Update the mock list rendering around itemKey to support
both function and string forms accepted by BubbleList: invoke function itemKey
values, and resolve string itemKey values from each item for the React key, with
the existing index fallback when itemKey is absent. Do not call non-function
itemKey values as functions.
In `@packages/x/components/bubble/demo/virtual-list.tsx`:
- Around line 18-24: Unify React Hook usage by importing useMemo alongside
useState in virtual-list.tsx and replacing all React.useState and React.useMemo
calls with the destructured hooks. Apply the same changes to the example code in
packages/x/components/bubble/demo/virtual-list.md lines 32-38 so the source and
documentation remain identical.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 0c7bdca5-7444-4b9c-92a5-b2b15c15410d
📒 Files selected for processing (17)
packages/x/components/bubble/BubbleList.tsxpackages/x/components/bubble/__tests__/list-benchmark.test.tsxpackages/x/components/bubble/__tests__/list-virtual.test.tsxpackages/x/components/bubble/demo/virtual-list.mdpackages/x/components/bubble/demo/virtual-list.tsxpackages/x/components/bubble/index.en-US.mdpackages/x/components/bubble/index.zh-CN.mdpackages/x/components/bubble/interface.tspackages/x/components/conversations/GroupTitle.tsxpackages/x/components/conversations/Item.tsxpackages/x/components/conversations/__tests__/virtual.test.tsxpackages/x/components/conversations/demo/virtual-list.mdpackages/x/components/conversations/demo/virtual-list.tsxpackages/x/components/conversations/index.en-US.mdpackages/x/components/conversations/index.tsxpackages/x/components/conversations/index.zh-CN.mdpackages/x/package.json
| // Mock @rc-component/virtual-list | ||
| jest.mock('@rc-component/virtual-list', () => { | ||
| const React = require('react'); | ||
| const MockVirtualList = React.forwardRef((props: any, ref: any) => { | ||
| React.useImperativeHandle(ref, () => ({ | ||
| scrollTo: jest.fn(), | ||
| nativeElement: null, | ||
| })); | ||
| const { data, children, itemKey, virtual, itemHeight, height, ...rest } = props; | ||
| return ( | ||
| <div data-testid="mock-virtual-list" {...rest}> | ||
| {data?.map((item: any, index: number) => ( | ||
| <div key={itemKey ? itemKey(item) : index}>{children(item, index)}</div> | ||
| ))} | ||
| </div> | ||
| ); | ||
| }); | ||
| return { __esModule: true, default: MockVirtualList }; | ||
| }); |
There was a problem hiding this comment.
🚀 Performance & Scalability | 🟠 Major | 🏗️ Heavy lift
补充使用真实 VirtualList 的大数据测试或基准。
此 mock 会同步渲染全部 1000 行,而当前断言仅验证组件未崩溃,无法发现虚拟窗口失效、动态高度错误或 DOM 节点数量退化。请至少增加一个不 mock 依赖的测试,验证 1000 条数据下仅渲染可视窗口,并记录基准指标。
Also applies to: 98-110
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/x/components/conversations/__tests__/virtual.test.tsx` around lines
6 - 24, 在 conversations 虚拟列表测试中补充至少一个不 mock `@rc-component/virtual-list` 的真实
VirtualList 测试,使用 1000 条数据验证仅渲染可视窗口而非全部行,并记录可用于检测 DOM 节点数量或渲染性能退化的基准指标;保留现有 mock
测试用于基础断言。
| {virtualProp ? ( | ||
| <> | ||
| <style>{` | ||
| .${prefixCls} { overflow-y: hidden !important; padding: 0 !important; } | ||
| .${prefixCls} .rc-virtual-list-scrollbar { display: none !important; } | ||
| .${prefixCls} .rc-virtual-list-holder { | ||
| overflow-y: auto !important; | ||
| padding: 12px; | ||
| box-sizing: border-box; | ||
| } | ||
| .${prefixCls} .rc-virtual-list-holder-inner > .${prefixCls}-item { | ||
| margin-top: 4px; | ||
| } | ||
| .${prefixCls} .rc-virtual-list-holder-inner > .${prefixCls}-group + .${prefixCls}-item { | ||
| margin-top: 0; | ||
| } | ||
| `}</style> |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
将虚拟模式样式限定到当前组件实例。
这些选择器直接修改所有同 prefixCls 的 Conversations。页面上只要存在一个虚拟实例,其他非虚拟实例也会被强制清除 padding、隐藏 overflow 和滚动条。请添加虚拟模式修饰类,并通过现有样式系统生成作用域规则。
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/x/components/conversations/index.tsx` around lines 352 - 368, 将
virtualProp 分支中的样式限定到当前 Conversations
实例:为该实例根元素添加虚拟模式修饰类,并通过现有样式系统生成包含该修饰类的作用域选择器。更新 style 中基于 prefixCls
的规则,使其仅匹配当前虚拟实例,避免影响其他非虚拟或不同实例。
…rwarding - flatData now respects collapsed state (enableCollapse + expandedKeys) - Create VirtualGroupTitle helper component to properly forward ref - Fixes CodeRabbit review feedback on PR ant-design#1979
…without mock - Map block to VirtualList align option, use nullish check for key - Add ResizeObserver to auto-scroll when content grows (streaming) - Remove VirtualList mock from benchmark, use real implementation - Fixes CodeRabbit review feedback on PR ant-design#1979
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/x/components/bubble/BubbleList.tsx (1)
324-328: 🩺 Stability & Availability | 🔴 Critical | 🏗️ Heavy lift
VirtualList不兼容flex-direction: column-reverse,会导致溢出时滚动完全失效。在 CSS Flexbox 机制中,如果对具备
overflow: auto的滚动容器应用flex-direction: column-reverse,当其内部内容的高度超过父容器时,超出的内容会向顶部溢出。这会导致浏览器的scrollTop锁定为0,并且用户无法向上滚动去查看历史的溢出内容。同时,
@rc-component/virtual-list所有的滚动事件监听、内容裁切以及节点位移计算(基于scrollTop和padding)都依赖标准的文档流正向排版。强行将它的容器翻转会导致虚拟列表的内部逻辑被彻底破坏。修复建议:
不要在虚拟列表上通过反转 Flex 容器方向来实现消息较少时的贴底。若需保持贴底对齐,可以考虑在高度不足时动态注入占位的padding-top,或者在启用virtual模式时放弃这个特定的短列表贴底表现以确保列表不发生滚动侧功能阻断。🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/x/components/bubble/BubbleList.tsx` around lines 324 - 328, Remove the conditional flexDirection: 'column-reverse' override from the style applied to VirtualList. Preserve the standard column layout and, if short-list bottom alignment is still required, implement it through padding or another VirtualList-compatible mechanism without reversing the scroll container.
🧹 Nitpick comments (2)
packages/x/components/bubble/BubbleList.tsx (1)
149-149: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value请将
virtualListRef补充上强类型。当前依然使用了
any,这会导致针对虚拟列表 API(如scrollTo的参数及nativeElement等)的调用缺乏类型保护。建议从@rc-component/virtual-list导入ListRef类型并应用。💻 建议的代码修改
首先,确保在文件顶部导入:
import type { ListRef } from '`@rc-component/virtual-list`';然后修改引用声明:
- const virtualListRef = React.useRef<any>(null); + const virtualListRef = React.useRef<ListRef>(null);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/x/components/bubble/BubbleList.tsx` at line 149, 为 BubbleList 组件中的 virtualListRef 移除 any 类型并补充 `@rc-component/virtual-list` 提供的 ListRef 类型;在文件顶部添加对应的类型导入,并保留 React ref 的 null 初始值。packages/x/components/bubble/__tests__/list-benchmark.test.tsx (1)
68-74: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value建议修改基准测试中的断言方式,避免受环境波动影响发生误判。
由于测试运行在
jsdom中,在此处断言渲染耗时< 10000ms预期过于宽松,既无法真实反映出虚拟列表的大数据量性能提升,也容易受 CI 环境整体负载的波动干扰。既然移除了 Mock 并引入了真实组件,更严谨有效的验证方法是:直接校验虚拟列表是否对 DOM 节点进行了裁剪(例如,验证实际挂载在文档中的
.ant-bubble节点数是否远小于给定的1000个),这比测量模拟环境下的渲染时间更有说服力。💡 建议的代码修改
- it('should measure render time for 1000 items in virtual mode', () => { + it('should mount reduced number of DOM nodes for 1000 items in virtual mode', () => { const items = generateLargeItems(1000); - const start = performance.now(); - render(<BubbleList items={items} virtual style={{ height: 600 }} />); - const end = performance.now(); - - // Should render without crash in reasonable time - expect(end - start).toBeLessThan(10000); + const { container } = render(<BubbleList items={items} virtual style={{ height: 600 }} />); + + // 验证虚拟列表对 DOM 节点进行了裁剪(实际挂载的节点应远少于 1000) + const bubbles = container.querySelectorAll('.ant-bubble'); + expect(bubbles.length).toBeGreaterThan(0); + expect(bubbles.length).toBeLessThan(1000); });🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/x/components/bubble/__tests__/list-benchmark.test.tsx` around lines 68 - 74, Replace the performance-duration assertion in the BubbleList benchmark test with a DOM virtualization assertion: after rendering the 1000-item list, query mounted .ant-bubble elements and verify their count is substantially below the total item count. Keep the real BubbleList rendering and virtual configuration unchanged, and remove the timing-based check.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/x/components/bubble/BubbleList.tsx`:
- Around line 247-260: Update the align mapping in the scroll handling logic
around virtualListRef.current.scrollTo to use only the VirtualList-supported
values 'top', 'bottom', or 'auto'. Ensure the default block behavior remains
compatible with bottom scrolling: when top is 'bottom' and block is omitted,
align the final item at the bottom rather than defaulting to top; preserve
explicit block alignment behavior for supported values.
- Around line 226-237: Update the ResizeObserver setup in the virtual-list
effect near virtualListRef to observe the internal .rc-virtual-list-holder-inner
content element instead of listRef.current, while preserving the existing
requestAnimationFrame scrollTo behavior and cleanup.
---
Outside diff comments:
In `@packages/x/components/bubble/BubbleList.tsx`:
- Around line 324-328: Remove the conditional flexDirection: 'column-reverse'
override from the style applied to VirtualList. Preserve the standard column
layout and, if short-list bottom alignment is still required, implement it
through padding or another VirtualList-compatible mechanism without reversing
the scroll container.
---
Nitpick comments:
In `@packages/x/components/bubble/__tests__/list-benchmark.test.tsx`:
- Around line 68-74: Replace the performance-duration assertion in the
BubbleList benchmark test with a DOM virtualization assertion: after rendering
the 1000-item list, query mounted .ant-bubble elements and verify their count is
substantially below the total item count. Keep the real BubbleList rendering and
virtual configuration unchanged, and remove the timing-based check.
In `@packages/x/components/bubble/BubbleList.tsx`:
- Line 149: 为 BubbleList 组件中的 virtualListRef 移除 any 类型并补充
`@rc-component/virtual-list` 提供的 ListRef 类型;在文件顶部添加对应的类型导入,并保留 React ref 的 null
初始值。
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 283a5591-702c-48ac-b2ee-bd6ab7b60407
📒 Files selected for processing (2)
packages/x/components/bubble/BubbleList.tsxpackages/x/components/bubble/__tests__/list-benchmark.test.tsx
…Scroll - align maps to VirtualList supported values: 'top' | 'bottom' | 'auto' - bottom scroll defaults to align: 'bottom' when block is omitted - ResizeObserver observes .rc-virtual-list-holder-inner instead of fixed container - Fixes CodeRabbit review feedback on PR ant-design#1979
Changes
virtualprop toBubbleListandConversations(default:false)@rc-component/virtual-listfor virtual scrollingforwardRefonConversationsItemandGroupTitleautoScrollto bottom in virtual mode forBubbleListRelated Issue
Closes #1948
Summary by CodeRabbit
virtual),支持按 key 定位、置顶/置底与自动滚动,并可在虚拟/普通模式间切换。virtual),在分组与大量数据场景下渲染更高效且保持激活态交互一致。virtual属性说明。