Skip to content

perf: 路由页渲染优化与 users 卡片间距#321

Merged
SurviveM merged 5 commits intoawsl-project:mainfrom
ymkiux:pr/users-route-optimizations
Mar 5, 2026
Merged

perf: 路由页渲染优化与 users 卡片间距#321
SurviveM merged 5 commits intoawsl-project:mainfrom
ymkiux:pr/users-route-optimizations

Conversation

@ymkiux
Copy link
Contributor

@ymkiux ymkiux commented Mar 5, 2026

Summary

  • 路由页:减少无效重渲染,统计更新节流
  • users 页:Card 增加外边距(m-6)

Testing

  • 未运行(未要求)

Summary by CodeRabbit

发布说明

  • 性能优化

    • 改进流式数据的节流与刷新逻辑,减少更新抖动并提升响应性能。
    • 增加多处稳定化与缓存优化,减少不必要的重新渲染。
  • 功能改进

    • 支持通过选项控制流式更新节流行为(可调节刷新频率)。
    • 搜索行为规范化并改进排序规则,结果更准确可靠。
    • 暴露可传入的搜索参数以便预置/同步查询。
  • UI 改进

    • 强化拖拽交互与索引处理,拖放体验更稳健。
    • 优化表格/行组件渲染以减少卡顿。
    • 调整用户页面卡片间距以优化视觉布局。

@coderabbitai
Copy link

coderabbitai bot commented Mar 5, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: dbc9be9d-044d-40eb-a20a-6f94531e257d

📥 Commits

Reviewing files that changed from the base of the PR and between 3c9af50 and 9769e2c.

📒 Files selected for processing (4)
  • web/src/components/routes/ClientTypeRoutesContent.tsx
  • web/src/hooks/use-streaming.ts
  • web/src/pages/client-routes/components/provider-row.tsx
  • web/src/pages/users/index.tsx
📜 Recent review details
🔇 Additional comments (6)
web/src/pages/users/index.tsx (1)

147-147: 样式改动符合预期。

m-6 的引入和本次“users 卡片间距优化”目标一致,改动范围清晰、风险低。

web/src/pages/client-routes/components/provider-row.tsx (2)

53-65: Memo 化改造方向正确。

将行组件与内容组件拆分为 base + memo 并配合自定义比较器,能够有效减少列表场景下的不必要重渲染,和本次性能目标一致。

Also applies to: 146-148, 233-249, 637-638


126-141: 详情弹窗按需渲染实现合理。

仅在 showDetailsDialogtrue 时挂载详情组件,能降低常态渲染负担。

web/src/hooks/use-streaming.ts (1)

24-27: 节流实现与切换刷新处理到位。

当前实现把状态写入统一收敛到 applyState,并在 throttleMs 变更时主动清理并同步缓冲状态,能避免节流切换导致的状态滞后。

Also applies to: 41-71, 143-151, 198-227

web/src/components/routes/ClientTypeRoutesContent.tsx (2)

61-97: 稳定化统计与索引映射优化有效。

ProviderStats 稳定引用和 itemsById/itemIndexById 的引入都很实用,降低了派生计算与查找成本,也更利于 memo 生效。

Also applies to: 125-126, 157-173, 257-275


211-213: 流式计数节流接入与拖拽防护实现良好。

根据列表规模动态设置节流,并在拖拽索引读取时做 undefined 防护,兼顾了性能与稳定性。

Also applies to: 321-326, 383-384, 409-415


📝 Walkthrough

Walkthrough

为客户端路由列表、流式请求 Hook 和提供商行组件引入性能与行为变更:添加 ClientTypeRoutesContent 的可选 searchQuery、稳定化 providerStats 与索引映射、useStreamingRequests 的可配置节流(throttleMs),以及 provider-row 组件的 React.memo 优化和按需细化渲染;并微调 users 页面样式。

Changes

Cohort / File(s) Summary
客户端路由内容优化
web/src/components/routes/ClientTypeRoutesContent.tsx
添加可选 searchQuery?: string;引入 ProviderStats 类型、isSameProviderStatsuseStableProviderStats;构建并 memo 化 providerByIdrouteByProviderIditemIndexByIditemsById;统一使用 normalizedQuery 搜索;调整排序、items 构建、拖拽结束逻辑与 DragOverlay 索引解析。
流式请求节流机制
web/src/hooks/use-streaming.ts
新增 StreamingOptionsthrottleMs?),useStreamingRequests(options) 支持节流;引入 activeRequestsRefflushTimerRefapplyStatescheduleFlush;在卸载与 throttleMs 变更时处理缓冲刷入;对外计数钩子签名扩展以接收 options。
提供商行组件记忆化
web/src/pages/client-routes/components/provider-row.tsx
SortableProviderRowProviderRowContent 拆分为 Base 实现并导出经 React.memo 包裹的组件,新增 areSortableProviderRowEqualareProviderRowContentEqual 自定义比较,设置 displayName;ProviderDetailsDialog 改为按需渲染。
用户页面样式调整
web/src/pages/users/index.tsx
为 Card 添加 m-6 外边距类(仅样式变更)。

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant UI as ClientTypeRoutesContent (UI)
  participant Hook as useStreamingRequests (Hook)
  participant Stream as StreamingSource
  participant Timer as ThrottleTimer

  UI->>Hook: 调用 useStreamingRequests({ throttleMs })
  Hook->>Stream: 订阅流更新
  Stream-->>Hook: 发送请求/状态更新
  Hook->>Hook: 更新 activeRequestsRef(缓冲)
  alt throttleMs > 0
    Hook->>Timer: scheduleFlush()
    Timer-->>Hook: 触发 applyState()
    Hook-->>UI: 推送已节流的状态
  else throttleMs == 0
    Hook-->>UI: 立即 applyState() 并推送状态
  end
  UI->>UI: 使用 stableProviderStats 与索引映射 更新渲染与拖拽行为
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 分钟

Possibly related PRs

Suggested reviewers

  • whhjdi
  • SurviveM
  • liril-net

Poem

🐰 搜索轻轻入耳旁,
节流稳住风与光,
记忆映射列整齐,
拖拽索引更分明,
兔子翻跃为你欢唱 🎉

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 46.15% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 标题涵盖了主要变更:路由页渲染优化(包括减少重渲染和统计更新节流)和 users 卡片间距调整,与改动内容相符。

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@web/src/hooks/use-streaming.ts`:
- Around line 136-149: When throttleMs toggles to 0 the existing cleanup path
clears flushTimerRef.current before the effect that should flush runs, so the
immediate flush is skipped; update the throttleMs effect to both clear the timer
and explicitly perform the flush (e.g., call setActiveRequests(new
Map(activeRequestsRef.current)) or invoke handleRequestUpdate) after clearing
flushTimerRef.current so the pending state is applied immediately; reference
throttleMs, flushTimerRef, setActiveRequests, activeRequestsRef and
handleRequestUpdate when making the change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: fc6fc216-68a2-4034-92d8-f293d8e8ab7d

📥 Commits

Reviewing files that changed from the base of the PR and between 3c9af50 and 2804759.

📒 Files selected for processing (4)
  • web/src/components/routes/ClientTypeRoutesContent.tsx
  • web/src/hooks/use-streaming.ts
  • web/src/pages/client-routes/components/provider-row.tsx
  • web/src/pages/users/index.tsx
📜 Review details
🔇 Additional comments (6)
web/src/pages/users/index.tsx (1)

147-147: Card 外边距调整与需求一致

这是纯展示层改动,满足 users 页间距优化目标,且不影响现有数据流与交互行为。

web/src/hooks/use-streaming.ts (1)

24-71: 节流状态与渲染状态分离实现合理

activeRequestsRefsetActiveRequests 分离,能把高频事件更新和 UI 提交解耦,整体优化方向正确。

Also applies to: 89-110

web/src/pages/client-routes/components/provider-row.tsx (2)

53-65: 行级组件 memo 化改造有效

SortableProviderRowProviderRowContent 的自定义比较 + memo 封装清晰,能直接减少列表重渲染压力。

Also applies to: 146-148, 233-249, 637-638


126-141: 详情弹窗按需挂载有助于渲染开销控制

仅在 showDetailsDialogtrue 时渲染 ProviderDetailsDialog,能避免大列表场景下无效子树渲染。

web/src/components/routes/ClientTypeRoutesContent.tsx (2)

61-97: Provider 统计稳定化实现到位

useStableProviderStats 与对 stableProviderStats 的消费路径打通后,能有效降低统计对象引用抖动带来的子组件重渲染。

Also applies to: 125-126, 396-397, 414-415


157-174: 索引与查找结构优化提升了拖拽健壮性

itemIds/itemsById/itemIndexById 的引入以及 undefined 边界判断,让拖拽索引解析更稳定,避免了潜在越界与空值路径。

Also applies to: 257-275, 321-326, 383-384

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.

🧹 Nitpick comments (2)
web/src/hooks/use-streaming.ts (2)

201-223: 设计提示:派生 hooks 不使用节流选项

这些派生 hooks 调用 useStreamingRequests() 时未传递 options,因此不会使用新的节流功能。如果组件同时使用 useStreamingRequests({ throttleMs: 100 }) 和这些派生 hooks,会创建独立的状态实例。

这是现有设计,如果未来需要统一节流行为,可考虑使用 React Context 共享单一的 streaming 状态实例。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/hooks/use-streaming.ts` around lines 201 - 223, Derived hooks
useClientStreamingCount, useProviderStreamingCount, and
useProviderClientStreamingCount call useStreamingRequests() without forwarding
options, so they create independent state when a caller uses
useStreamingRequests({ throttleMs: ... }). Update these derived hooks to accept
an optional options parameter (or accept and forward an existing options object)
and pass it through to useStreamingRequests(options); alternatively, if you
intend a single shared instance, refactor useStreamingRequests to expose a React
Context and have these hooks read the shared context instead of calling
useStreamingRequests directly—choose one approach and apply it consistently
across useClientStreamingCount, useProviderStreamingCount, and
useProviderClientStreamingCount.

143-154: 已修复之前的 review 问题,逻辑正确。

throttleMs 切换到 0 时,现在会正确地清除定时器并立即刷出缓冲状态,避免丢失更新。

关于 void handleRequestUpdate; 这一行:这是为了满足 exhaustive-deps 规则而使用的模式。由于 handleRequestUpdate 的依赖链最终只依赖于 throttleMs,实际上这个依赖是冗余的。建议考虑使用 // eslint-disable-next-line react-hooks/exhaustive-deps 注释来明确说明意图,或移除这个依赖。

可选:简化依赖数组
  useEffect(() => {
    if (throttleMs <= 0) {
      if (flushTimerRef.current) {
        clearTimeout(flushTimerRef.current);
        flushTimerRef.current = null;
      }
      // 关闭节流时立即刷出缓冲状态,避免清理定时器后丢失一次更新。
      setActiveRequests(new Map(activeRequestsRef.current));
    }
-    // 让 effect 与最新的 handleRequestUpdate 逻辑保持一致(依赖变更触发重新刷出)。
-    void handleRequestUpdate;
-  }, [throttleMs, handleRequestUpdate]);
+  }, [throttleMs]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/hooks/use-streaming.ts` around lines 143 - 154, The current useEffect
includes a no-op "void handleRequestUpdate;" to satisfy exhaustive-deps;
instead, explicitly document the intent by either removing handleRequestUpdate
from the dependency list and adding a comment to suppress the linter or keep it
and add a eslint disable line: update the effect so that when throttleMs becomes
0 it still clears flushTimerRef.current and calls setActiveRequests(new
Map(activeRequestsRef.current)), and then replace the "void
handleRequestUpdate;" trick with a clear directive like "//
eslint-disable-next-line react-hooks/exhaustive-deps" or remove
handleRequestUpdate from the deps array to avoid the redundant dependency while
preserving behavior of throttleMs, flushTimerRef, setActiveRequests,
activeRequestsRef, and handleRequestUpdate.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@web/src/hooks/use-streaming.ts`:
- Around line 201-223: Derived hooks useClientStreamingCount,
useProviderStreamingCount, and useProviderClientStreamingCount call
useStreamingRequests() without forwarding options, so they create independent
state when a caller uses useStreamingRequests({ throttleMs: ... }). Update these
derived hooks to accept an optional options parameter (or accept and forward an
existing options object) and pass it through to useStreamingRequests(options);
alternatively, if you intend a single shared instance, refactor
useStreamingRequests to expose a React Context and have these hooks read the
shared context instead of calling useStreamingRequests directly—choose one
approach and apply it consistently across useClientStreamingCount,
useProviderStreamingCount, and useProviderClientStreamingCount.
- Around line 143-154: The current useEffect includes a no-op "void
handleRequestUpdate;" to satisfy exhaustive-deps; instead, explicitly document
the intent by either removing handleRequestUpdate from the dependency list and
adding a comment to suppress the linter or keep it and add a eslint disable
line: update the effect so that when throttleMs becomes 0 it still clears
flushTimerRef.current and calls setActiveRequests(new
Map(activeRequestsRef.current)), and then replace the "void
handleRequestUpdate;" trick with a clear directive like "//
eslint-disable-next-line react-hooks/exhaustive-deps" or remove
handleRequestUpdate from the deps array to avoid the redundant dependency while
preserving behavior of throttleMs, flushTimerRef, setActiveRequests,
activeRequestsRef, and handleRequestUpdate.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4768fc7c-1e2c-4a93-9aab-773064cd7fd1

📥 Commits

Reviewing files that changed from the base of the PR and between 2804759 and ae6543b.

📒 Files selected for processing (1)
  • web/src/hooks/use-streaming.ts
📜 Review details
🔇 Additional comments (3)
web/src/hooks/use-streaming.ts (3)

24-27: LGTM!

接口定义清晰,throttleMs 默认值为 0(不节流)是合理的默认行为。


48-71: LGTM!

节流逻辑实现正确:

  • scheduleFlush 使用早返回避免重复创建定时器
  • applyState 正确地通过 activeRequestsRef 避免闭包陈旧问题,并根据 throttleMs 路由到立即更新或延迟刷新

96-110: LGTM!

handleRequestUpdate 正确使用 activeRequestsRef.current 避免闭包陈旧问题,并通过 new Map() 保证状态不可变性。

@ymkiux
Copy link
Contributor Author

ymkiux commented Mar 5, 2026

@coderabbitai re-review

@coderabbitai
Copy link

coderabbitai bot commented Mar 5, 2026

@ymkiux: Sure! I'll re-review the PR now.

✅ Actions performed

Full review triggered.

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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@web/src/hooks/use-streaming.ts`:
- Around line 133-153: When throttleMs changes we must flush any buffered state
even when the new value is still >0 to avoid dropping pending updates; update
the useEffect that depends on throttleMs to check for an existing
flushTimerRef.current (and clearTimeout it and null it) and then immediately
call setActiveRequests(new Map(activeRequestsRef.current)) so the buffered
activeRequests are committed on any throttleMs change; reference flushTimerRef,
throttleMs, setActiveRequests and activeRequestsRef to locate and modify the
effect and ensure cleanup still clears the timer.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 3289267f-5ebb-437f-ae95-b44e4ad912b0

📥 Commits

Reviewing files that changed from the base of the PR and between ae6543b and 33a1bd5.

📒 Files selected for processing (1)
  • web/src/hooks/use-streaming.ts

@ymkiux
Copy link
Contributor Author

ymkiux commented Mar 5, 2026

@coderabbitai re-review

@coderabbitai
Copy link

coderabbitai bot commented Mar 5, 2026

@ymkiux: Sure! I'll re-review the PR now.

✅ Actions performed

Full review triggered.

@SurviveM SurviveM merged commit af22328 into awsl-project:main Mar 5, 2026
2 checks passed
dreamhunter2333 pushed a commit that referenced this pull request Mar 6, 2026
* perf: 降低路由页重渲染

* style: users 卡片外边距

* fix: 关闭节流立即刷新

* fix: 透传 streaming 参数

* fix: 节流切换立即刷新
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