Skip to content

chore: replace flashlist with legendlist#281

Draft
roitium wants to merge 1 commit into
devfrom
codex/replace-flashlist-with-legendlist
Draft

chore: replace flashlist with legendlist#281
roitium wants to merge 1 commit into
devfrom
codex/replace-flashlist-with-legendlist

Conversation

@roitium

@roitium roitium commented May 19, 2026

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

发布说明

  • 依赖升级

    • 更新列表组件库,优化应用列表渲染性能
  • 改进

    • 升级评论、下载管理、历史记录、播放列表等多个列表页面的渲染性能
    • 统一列表组件实现,提升整体应用流畅度
  • 文档

    • 更新开发规范文档,明确列表组件最佳实践

Review Change Stack

@roitium roitium added the enhancement New feature or request label May 19, 2026
@safedep

safedep Bot commented May 19, 2026

Copy link
Copy Markdown

SafeDep Report Summary

Green Malicious Packages Badge Green Vulnerable Packages Badge Green Risky License Badge

Package Details
Package Malware Vulnerability Risky License Report
icon @legendapp/list @ 3.0.0-beta.56
apps/mobile/package.json pnpm-lock.yaml
ok icon
ok icon
ok icon
🔗

View complete scan results →

This report is generated by SafeDep Github App

@vercel

vercel Bot commented May 19, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
bbplayer-docs Ready Ready Preview, Comment May 19, 2026 1:05pm

@roitium

roitium commented May 19, 2026

Copy link
Copy Markdown
Collaborator Author

@coderabbitai /pause

@coderabbitai

coderabbitai Bot commented May 19, 2026

Copy link
Copy Markdown

Note

Reviews paused

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

概览

本PR将整个应用的虚拟列表实现从 FlashList (v1, @shopify/flash-list) 迁移到 LegendList (v3.0.0-beta.56, @legendapp/list)。涉及依赖替换、类型系统更新、开发约定文档修订,以及 20+ 个文件的组件renderItem、extraData结构和列表配置的适配。

变更

列表组件全应用迁移:FlashList → LegendList

Layer / File(s) Summary
依赖和文档约定
apps/mobile/package.json, apps/mobile/src/types/list.ts, AGENTS.md, apps/mobile/AGENTS.md, apps/mobile/docs/BEST_PRACTICES.md, apps/mobile/docs/CONTRIBUTING.md, apps/mobile/docs/Home.md
移除 @shopify/flash-list 依赖,新增 @legendapp/list@3.0.0-beta.56;更新 ListRenderItemInfoWithExtraData 类型别名为 LegendListRenderItemPropsWithExtraData;更新AGENTS和BEST_PRACTICES文档,规定 LegendList 的 keyExtractor 必填、renderItem 静态化、extraData 使用 useMemo、estimatedItemSize 配置以及 recycleItems 的启用条件。
基础工具函数迁移
apps/mobile/src/hooks/ui/useDoubleTapScrollToTop.ts, apps/mobile/src/features/downloads/DownloadTaskItem.tsx
更新 useDoubleTapScrollToTop 的签名,将 FlashListRef<T> 改为 LegendListRef,并在 scrollToOffset 调用前添加 void;简化 useRecyclingState 的调用,仅传入初始化参数。
简单列表替换(无extraData复杂性)
apps/mobile/src/app/download.tsx, apps/mobile/src/app/downloaded.tsx, apps/mobile/src/app/history/[date].tsx, apps/mobile/src/app/history/overall.tsx, apps/mobile/src/features/library/collection/CollectionList.tsx, apps/mobile/src/features/library/favorite/FavoriteFolderList.tsx, apps/mobile/src/features/library/local/LocalPlaylistList.tsx, apps/mobile/src/features/library/multipage/MultiPageVideosList.tsx
在所有列表渲染处将 FlashList 替换为 LegendList,保持原有 data/renderItem/keyExtractor/extraData 传参一致,统一新增 estimatedItemSize 配置(值域从 56-120 不等)。
评论列表extraData重构
apps/mobile/src/app/comments/[bvid].tsx, apps/mobile/src/app/comments/reply.tsx
更新 renderItem 参数类型为 LegendListRenderItemPropsWithExtraData,调整从 extraData 直接读取 bvidonReplyPress 的方式,新增 estimatedItemSize={120}
播放器队列Modal
apps/mobile/src/components/modals/PlayerQueueModal.tsx
新增 QueueItemExtraData 类型,重构 renderItemextraData 读取 currentTrackId/switchTrackHandler/removeTrackHandler;新增 extraData 的 useMemo,将列表 ref 类型改为 LegendListRef,补充 estimatedItemSize 配置。
歌词选择与搜索Modal
apps/mobile/src/components/modals/lyrics/ManualSearchLyrics.tsx, apps/mobile/src/components/modals/player/LyricsSelectionModal.tsx
新增 LyricItemExtraData 类型,搜索结果列表和歌词选择列表均改为从 extraData 驱动 isSelected/toggleSelection 状态;使用 useMemo 构建 extraData,实现 renderItem 的静态化,各自补充对应的 estimatedItemSize
歌单Modal列表替换
apps/mobile/src/components/modals/playlist/BatchAddTracksToLocalPlaylist.tsx, apps/mobile/src/components/modals/playlist/ManualMatchExternalSync.tsx, apps/mobile/src/components/modals/playlist/MergePlaylistsModal.tsx, apps/mobile/src/components/modals/playlist/UpdateTrackLocalPlaylistsModal.tsx
统一更新 renderItem 参数类型为 LegendListRenderItemPropsWithExtraData,简化 extraData 的构造,移除分页相关回调(ManualMatchExternalSync),各列表新增 estimatedItemSize
本地曲目列表组件
apps/mobile/src/features/playlist/local/components/LocalTrackList.tsx
更新 LocalTrackListProps 继承基类从 FlashListProps<Track> 改为 LegendListProps<Track>,将 listRef 类型改为 LegendListRef | null,调整 renderItem 的 extraData 解构方式,新增 estimatedItemSize={70}recycleItems 配置。
外部歌单同步页面
apps/mobile/src/app/playlist/external-sync.tsx
更新 renderItem 参数类型和 extraData 结构,移除 syncing 字段,仅保留 openManualMatch;新增 extraData 的 useMemo 优化,补充 estimatedItemSize={96}
远程曲目列表组件
apps/mobile/src/features/playlist/remote/components/RemoteTrackList.tsx
扩展 TrackListProps 接口,新增 highlightTrackId?: string 属性;扩展 ExtraData 接口,新增 highlightTrackIdcurrentTrackIdRef;更新 renderItemDefault 参数类型,重构 extraData 的 useMemo 以支持新的高亮和当前曲目判断逻辑,补充 estimatedItemSize={72}
待看列表项类型适配
apps/mobile/src/features/playlist/remote/toview/components/Item.tsx
更新 renderToViewItem 的参数类型签名,从 ListRenderItemInfoWithExtraData 改为 LegendListRenderItemPropsWithExtraData
多页远程播放列表
apps/mobile/src/app/playlist/remote/multipage/[bvid].tsx
renderCustomItem 从组件内部 useCallback 提取为文件级独立函数,通过 extraData.highlightTrackId 驱动闪烁效果;更新 listRef 类型为 LegendListRef,在 TrackList 上显式传入 highlightTrackId={cid}
本地播放列表滚动更新
apps/mobile/src/app/playlist/local/[id].tsx
更新滚动相关注释从"Current FlashList scroll offset"改为"Current LegendList scroll offset";在 scrollToOffset 调用前添加 void 前缀以处理返回值。
欢迎Modal导入整理
apps/mobile/src/components/modals/app/WelcomeModal.tsx
重排 react-native-reanimatedButtonFunctionalMenu 的导入顺序,无功能变更。

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

相关PR

  • bbplayer-app/BBPlayer#119:最初在评论页面中引入 FlashList,本PR在这些同一页面中替换为 LegendList。
  • bbplayer-app/BBPlayer#150:之前添加的多页远程播放列表高亮功能,本PR通过新的 highlightTrackId extraData 方式重构。
  • bbplayer-app/BBPlayer#173:远程曲目列表组件(RemoteTrackList.tsx)的 currentTrackIdRef 相关调整与本PR直接关联。

🐰 千千列表已归心,
从闪光到传奇光,
extraData 调调整,
estimatedItemSize 配配妥!
新库新气象,回收智慧藏。

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ 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%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR标题准确概括了主要变更:用LegendList替换FlashList库,涉及包管理、文档和多个组件的更新。
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/replace-flashlist-with-legendlist

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.

@socket-security

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​babel/​core@​7.28.6971008096100

View full report

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (3)
apps/mobile/AGENTS.md (1)

100-113: ⚡ Quick win

LegendList 示例建议去除对可变选中态的 extraData 依赖(Line 100)

当前示例把 selectedId 作为 extraData 传给 renderItem,会和迁移约束产生冲突。建议改成 item 组件直接读取状态源(如 store/selector),extraData 仅保留真正无法就地读取的静态依赖。

As per coding guidelines, “ensure components can read state directly instead of relying on extraData for mutable state”.

🤖 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 `@apps/mobile/AGENTS.md` around lines 100 - 113, The example passes a mutable
selectedId via extraData to LegendList
(renderPlaylistItem/extraData/selectedId), which conflicts with migration rules;
change renderPlaylistItem to only pass item/playlist to PlaylistItem and remove
extraData/useMemo and the extraData prop on LegendList, then make PlaylistItem
read selectedId from the canonical state source (store/selector) internally so
mutable selection is not forwarded via LegendList.extraData; retain extraData
only for truly static dependencies if needed.
apps/mobile/src/components/modals/player/LyricsSelectionModal.tsx (1)

362-369: ⚡ Quick win

避免通过 extraData 传递可变选择状态。

当前将 selectedIndicestoggleSelection 放入 extraData 中传给 LegendList,违反了编码规范。根据 @legendapp/list/react-native 官方文档,不应通过 extraData 传递会变化的选择状态,因为这会导致列表中所有条目都重新渲染。建议让条目组件直接读取选择状态(例如通过 React Context 或状态库 selector),extraData 仅用于传递稳定的、不变的数据。

适用行数: 362-369, 524-529

🤖 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 `@apps/mobile/src/components/modals/player/LyricsSelectionModal.tsx` around
lines 362 - 369, The extraData object passed to LegendList currently includes
mutable selection state (selectedIndices and toggleSelection), causing full-list
re-renders; update the implementation so extraData only contains stable values
(primaryColor, onSurfaceColor, onSurfaceVariantColor) and remove selectedIndices
and toggleSelection from it, then move selection access into the list item
component (e.g., the lyric/LegendList item) via a stable selector or React
Context/state hook so each item reads selection directly; adjust references to
extraData in LegendList usage and ensure item renderer uses the new selection
source instead of relying on extraData.
apps/mobile/src/features/playlist/local/components/LocalTrackList.tsx (1)

299-334: 🏗️ Heavy lift

收敛 extraData,避免承载高频可变状态。

当前 extraData 包含 selectiondownloadStatusisOfflineplayableOfflineKeysinsertAfterIndexcolors 等高频变化字段,导致每次状态更新时整个列表重新渲染。建议将这些可变状态下沉到 TrackListItem 组件内部直接读取(例如通过 React Context 或状态库 selector),extraData 仅保留稳定的配置项(如回调函数)。这样可避免不必要的全表重渲染,提升列表性能。

编码指南要求:"ensure components can read state directly instead of relying on extraData for mutable state"。

🤖 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 `@apps/mobile/src/features/playlist/local/components/LocalTrackList.tsx` around
lines 299 - 334, The extraData object in LocalTrackList is carrying
high-frequency mutable state (selection, downloadStatus, isOffline,
playableOfflineKeys, insertAfterIndex, colors) which forces full list
re-renders; update extraData to only include stable configuration and callbacks
(handleTrackPress, handleMenuPress, onDragStart/onDragUpdate/onDragEnd,
isReadOnly, etc.), remove selection, downloadStatus, isOffline,
playableOfflineKeys, insertAfterIndex, and theme.colors from the memo
dependency; then modify TrackListItem to read those removed mutable pieces
directly (via React Context or your state selector library) so TrackListItem
subscribes to changes locally per the "ensure components can read state directly
instead of relying on extraData for mutable state" guideline.
🤖 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 `@AGENTS.md`:
- Around line 191-199: The example uses extraData to carry selection state which
conflicts with the guideline; update the sample so the Item component (or a
shared store) reads selection directly instead of relying on extraData: change
renderItem so it derives selected via item or a selector (e.g., Item reads
item.selected or a selection store) and remove the extraData prop from the
LegendList call; keep items, renderItem, Item, LegendList and keyExtractor names
so reviewers can locate and verify the change.

In `@apps/mobile/docs/BEST_PRACTICES.md`:
- Around line 10-11: The guideline text for LegendList is too prescriptive about
passing external state via extraData; update the wording to relax the rule:
state that renderItem should be defined outside the component (renderItem) and
that extraData should be memoized with useMemo when used, but clarify that
components should prefer reading state directly and only pass necessary mutable
values via extraData when required. Locate references to LegendList, renderItem,
extraData, and useMemo in the BEST_PRACTICES.md content and change the sentence
"依赖的外部状态统一通过 `extraData` 传入" to a softer recommendation that prefers direct
state access and reserves `extraData` for necessary cases.

In `@apps/mobile/src/app/downloaded.tsx`:
- Around line 533-538: The list currently passes mutable selection state
(selectMode, selected) inside extraData to LegendList which causes whole-list
re-renders; instead remove any mutable selection fields from extraData and keep
only stable callbacks (e.g., onToggleSelect, onSelectAll) there, then have
DownloadedItem read the live selection state directly (via context, a hook, or a
parent prop that doesn't recreate an object) so selection changes don't recreate
extraData and trigger full list recomputation; update LegendList usage and
DownloadedItem to consume the selection state from the chosen source and only
use extraData for stable callbacks.

In `@apps/mobile/src/app/playlist/remote/multipage/`[bvid].tsx:
- Around line 68-75: The renderCustomItem currently reads mutable UI state
(highlightTrackId) from extraData; instead, refactor to a named function (e.g.,
export or const renderItemName) that closes over the component's local state
(use cid or component-level highlight id) and remove highlightTrackId from the
extraData contract so extraData only carries static callbacks/config; update the
LegendList usage to pass the named renderItem and ensure any place referencing
renderCustomItem (also at the other occurrence) is updated to use the new named
function and component state read instead of extraData.

In `@apps/mobile/src/components/modals/PlayerQueueModal.tsx`:
- Around line 104-108: Move the LegendList renderItem into the PlayerQueueModal
component and stop passing currentTrackId via extraData: define a named
renderItem function inside PlayerQueueModal (e.g., const renderItem =
useCallback(( {item, index} ) => { ... }, [currentTrackId, switchTrackHandler,
removeTrackHandler])) so it captures currentTrackId, switchTrackHandler and
removeTrackHandler from the closure; update the LegendList usage to reference
this named renderItem and remove currentTrackId from the
QueueItemExtraData/extraData payload (or eliminate extraData entirely) and
ensure all references to renderItem and extraData (previously at the LegendList
call) are updated accordingly.

---

Nitpick comments:
In `@apps/mobile/AGENTS.md`:
- Around line 100-113: The example passes a mutable selectedId via extraData to
LegendList (renderPlaylistItem/extraData/selectedId), which conflicts with
migration rules; change renderPlaylistItem to only pass item/playlist to
PlaylistItem and remove extraData/useMemo and the extraData prop on LegendList,
then make PlaylistItem read selectedId from the canonical state source
(store/selector) internally so mutable selection is not forwarded via
LegendList.extraData; retain extraData only for truly static dependencies if
needed.

In `@apps/mobile/src/components/modals/player/LyricsSelectionModal.tsx`:
- Around line 362-369: The extraData object passed to LegendList currently
includes mutable selection state (selectedIndices and toggleSelection), causing
full-list re-renders; update the implementation so extraData only contains
stable values (primaryColor, onSurfaceColor, onSurfaceVariantColor) and remove
selectedIndices and toggleSelection from it, then move selection access into the
list item component (e.g., the lyric/LegendList item) via a stable selector or
React Context/state hook so each item reads selection directly; adjust
references to extraData in LegendList usage and ensure item renderer uses the
new selection source instead of relying on extraData.

In `@apps/mobile/src/features/playlist/local/components/LocalTrackList.tsx`:
- Around line 299-334: The extraData object in LocalTrackList is carrying
high-frequency mutable state (selection, downloadStatus, isOffline,
playableOfflineKeys, insertAfterIndex, colors) which forces full list
re-renders; update extraData to only include stable configuration and callbacks
(handleTrackPress, handleMenuPress, onDragStart/onDragUpdate/onDragEnd,
isReadOnly, etc.), remove selection, downloadStatus, isOffline,
playableOfflineKeys, insertAfterIndex, and theme.colors from the memo
dependency; then modify TrackListItem to read those removed mutable pieces
directly (via React Context or your state selector library) so TrackListItem
subscribes to changes locally per the "ensure components can read state directly
instead of relying on extraData for mutable state" guideline.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: be021e58-5514-46ec-ac35-2da7acbe2a2e

📥 Commits

Reviewing files that changed from the base of the PR and between 53fbc28 and 960c181.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (33)
  • AGENTS.md
  • apps/mobile/AGENTS.md
  • apps/mobile/docs/BEST_PRACTICES.md
  • apps/mobile/docs/CONTRIBUTING.md
  • apps/mobile/docs/Home.md
  • apps/mobile/package.json
  • apps/mobile/src/app/comments/[bvid].tsx
  • apps/mobile/src/app/comments/reply.tsx
  • apps/mobile/src/app/download.tsx
  • apps/mobile/src/app/downloaded.tsx
  • apps/mobile/src/app/history/[date].tsx
  • apps/mobile/src/app/history/overall.tsx
  • apps/mobile/src/app/playlist/external-sync.tsx
  • apps/mobile/src/app/playlist/local/[id].tsx
  • apps/mobile/src/app/playlist/remote/multipage/[bvid].tsx
  • apps/mobile/src/components/modals/PlayerQueueModal.tsx
  • apps/mobile/src/components/modals/app/WelcomeModal.tsx
  • apps/mobile/src/components/modals/lyrics/ManualSearchLyrics.tsx
  • apps/mobile/src/components/modals/player/LyricsSelectionModal.tsx
  • apps/mobile/src/components/modals/playlist/BatchAddTracksToLocalPlaylist.tsx
  • apps/mobile/src/components/modals/playlist/ManualMatchExternalSync.tsx
  • apps/mobile/src/components/modals/playlist/MergePlaylistsModal.tsx
  • apps/mobile/src/components/modals/playlist/UpdateTrackLocalPlaylistsModal.tsx
  • apps/mobile/src/features/downloads/DownloadTaskItem.tsx
  • apps/mobile/src/features/library/collection/CollectionList.tsx
  • apps/mobile/src/features/library/favorite/FavoriteFolderList.tsx
  • apps/mobile/src/features/library/local/LocalPlaylistList.tsx
  • apps/mobile/src/features/library/multipage/MultiPageVideosList.tsx
  • apps/mobile/src/features/playlist/local/components/LocalTrackList.tsx
  • apps/mobile/src/features/playlist/remote/components/RemoteTrackList.tsx
  • apps/mobile/src/features/playlist/remote/toview/components/Item.tsx
  • apps/mobile/src/hooks/ui/useDoubleTapScrollToTop.ts
  • apps/mobile/src/types/list.ts

Comment thread AGENTS.md
Comment on lines +191 to +199
const renderItem = ({ item, extraData }) => (
<Item item={item} selected={extraData.selected.has(item.id)} />
)

// Use with memoized extraData
<FlashList
<LegendList
data={items}
renderItem={renderItem}
extraData={useMemo(() => ({ selected }), [selected])}
extraData={extraData}
keyExtractor={keyExtractor}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

文档示例与约束方向不一致(Line 191)

这里示例仍把选中态放在 extraData 里,和上方“避免把可直接读取的状态放入 extraData”的规则方向冲突。建议把示例改为 item 组件/Store 直接读取状态,避免误导后续实现。

As per coding guidelines, “ensure components can read state directly instead of relying on extraData for mutable state”.

🤖 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 `@AGENTS.md` around lines 191 - 199, The example uses extraData to carry
selection state which conflicts with the guideline; update the sample so the
Item component (or a shared store) reads selection directly instead of relying
on extraData: change renderItem so it derives selected via item or a selector
(e.g., Item reads item.selected or a selection store) and remove the extraData
prop from the LegendList call; keep items, renderItem, Item, LegendList and
keyExtractor names so reviewers can locate and verify the change.

Comment on lines +10 to +11
2. **renderItem 静态化**: `renderItem` 函数必须定义在组件外部,依赖的外部状态统一通过 `extraData` 传入,避免在组件内用闭包创建 render 函数。
3. **extraData memo 化**: 传给 LegendList 的 `extraData` 必须使用 `useMemo`,不要传 inline object。

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

LegendList 规范表述建议收敛:不要“统一”通过 extraData 传状态(Line 10)

“依赖外部状态统一通过 extraData 传入”这句过强,容易导致把可变状态都塞进 extraData。建议改成:优先让 item 直接读取状态,仅在必要时再使用 extraData

As per coding guidelines, “ensure components can read state directly instead of relying on extraData for mutable state”.

🤖 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 `@apps/mobile/docs/BEST_PRACTICES.md` around lines 10 - 11, The guideline text
for LegendList is too prescriptive about passing external state via extraData;
update the wording to relax the rule: state that renderItem should be defined
outside the component (renderItem) and that extraData should be memoized with
useMemo when used, but clarify that components should prefer reading state
directly and only pass necessary mutable values via extraData when required.
Locate references to LegendList, renderItem, extraData, and useMemo in the
BEST_PRACTICES.md content and change the sentence "依赖的外部状态统一通过 `extraData` 传入"
to a softer recommendation that prefers direct state access and reserves
`extraData` for necessary cases.

Comment on lines +533 to +538
<LegendList
data={filteredTasks}
renderItem={renderDownloadedItem}
extraData={extraData}
keyExtractor={(item) => item.id}
estimatedItemSize={72}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

# First, let's locate and examine the downloaded.tsx file
fd -n "downloaded.tsx" -t f

Repository: bbplayer-app/BBPlayer

Length of output: 296


🏁 Script executed:

# Get the file size to understand how much content we're dealing with
wc -l apps/mobile/src/app/downloaded.tsx

Repository: bbplayer-app/BBPlayer

Length of output: 104


🏁 Script executed:

# Read the specific lines mentioned in the review (533-538) and surrounding context
sed -n '500,560p' apps/mobile/src/app/downloaded.tsx | cat -n

Repository: bbplayer-app/BBPlayer

Length of output: 1907


🏁 Script executed:

# Search for the extraData variable definition
rg -n "extraData\s*=" apps/mobile/src/app/downloaded.tsx | head -20

Repository: bbplayer-app/BBPlayer

Length of output: 153


🏁 Script executed:

# Search for renderDownloadedItem function
rg -n "renderDownloadedItem|const renderDownloadedItem" apps/mobile/src/app/downloaded.tsx -A 15

Repository: bbplayer-app/BBPlayer

Length of output: 963


🏁 Script executed:

# Search for DownloadedItem component
rg -n "DownloadedItem" apps/mobile/src/app/downloaded.tsx | head -20

Repository: bbplayer-app/BBPlayer

Length of output: 329


🏁 Script executed:

# Get more context around the beginning of the file to understand state management
head -200 apps/mobile/src/app/downloaded.tsx | tail -150 | cat -n

Repository: bbplayer-app/BBPlayer

Length of output: 4486


🏁 Script executed:

# Look at the extraData definition around line 434
sed -n '420,450p' apps/mobile/src/app/downloaded.tsx | cat -n

Repository: bbplayer-app/BBPlayer

Length of output: 1132


🏁 Script executed:

# Check if there's any state management (useState, useContext, etc.) at the top of the component
sed -n '200,300p' apps/mobile/src/app/downloaded.tsx | cat -n

Repository: bbplayer-app/BBPlayer

Length of output: 3425


🏁 Script executed:

# Look for the main component function definition and its state management
sed -n '300,450p' apps/mobile/src/app/downloaded.tsx | cat -n

Repository: bbplayer-app/BBPlayer

Length of output: 4934


🏁 Script executed:

# Look at the end of the extraData useMemo to see the dependency array
sed -n '430,470p' apps/mobile/src/app/downloaded.tsx | cat -n

Repository: bbplayer-app/BBPlayer

Length of output: 1254


🏁 Script executed:

# Search for useTrackSelection hook to understand how state is managed
fd "useTrackSelection" -t f apps/mobile/src/

Repository: bbplayer-app/BBPlayer

Length of output: 200


🏁 Script executed:

# Check if there's a context provider or wrapper
rg -n "useTrackSelection|TrackSelection" apps/mobile/src/ -l

Repository: bbplayer-app/BBPlayer

Length of output: 731


🏁 Script executed:

# Check the useTrackSelection hook to see if state could be accessed directly
cat apps/mobile/src/features/playlist/local/hooks/useTrackSelection.ts

Repository: bbplayer-app/BBPlayer

Length of output: 984


🏁 Script executed:

# Check how other components (like in playlist) use LegendList
rg -n "LegendList" apps/mobile/src/app/playlist/ -B 5 -A 10 | head -100

Repository: bbplayer-app/BBPlayer

Length of output: 8636


将可变选择态从 extraData 迁出,改由 DownloadedItem 直接读取

当前在 extraData 中包含 selectModeselected 等可变状态,导致这些状态变化时会重新生成 extraData 对象,进而驱动整个列表重新渲染。根据 LegendList 组件约定,应让 DownloadedItem 直接访问这些状态(例如通过上下文),仅在 extraData 中保留稳定的回调函数。这样可避免因选择态变化而产生不必要的列表重算。

🤖 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 `@apps/mobile/src/app/downloaded.tsx` around lines 533 - 538, The list
currently passes mutable selection state (selectMode, selected) inside extraData
to LegendList which causes whole-list re-renders; instead remove any mutable
selection fields from extraData and keep only stable callbacks (e.g.,
onToggleSelect, onSelectAll) there, then have DownloadedItem read the live
selection state directly (via context, a hook, or a parent prop that doesn't
recreate an object) so selection changes don't recreate extraData and trigger
full list recomputation; update LegendList usage and DownloadedItem to consume
the selection state from the chosen source and only use extraData for stable
callbacks.

Comment on lines +68 to +75
const renderCustomItem = ({
item,
index,
extraData,
}: LegendListRenderItemPropsWithExtraData<BilibiliTrack, ExtraData>) => {
if (!extraData) throw new Error('Extradata 不存在')
const shouldFlash = String(item.id) === extraData.highlightTrackId

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

避免通过 extraData 传递高亮这类可变 UI 状态。

这里把 highlightTrackId 作为 extraData 驱动项传入 renderCustomItem,与当前 LegendList 约定不一致。建议让 renderItem 直接读取组件内状态(如闭包读取 cid),extraData 仅保留稳定回调/静态配置。

As per coding guidelines For LegendList components, define renderItem as a named function (not inline anonymous) and ensure components can read state directly instead of relying on extraData for mutable state.

Also applies to: 290-290

🤖 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 `@apps/mobile/src/app/playlist/remote/multipage/`[bvid].tsx around lines 68 -
75, The renderCustomItem currently reads mutable UI state (highlightTrackId)
from extraData; instead, refactor to a named function (e.g., export or const
renderItemName) that closes over the component's local state (use cid or
component-level highlight id) and remove highlightTrackId from the extraData
contract so extraData only carries static callbacks/config; update the
LegendList usage to pass the named renderItem and ensure any place referencing
renderCustomItem (also at the other occurrence) is updated to use the new named
function and component state read instead of extraData.

Comment on lines +104 to +108
type QueueItemExtraData = {
currentTrackId?: string
switchTrackHandler: (index: number) => Promise<void>
removeTrackHandler: (index: number) => Promise<void>
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# 验证 PlayerQueueModal 是否通过 extraData 传递可变状态 currentTrackId
rg -n -C2 "type QueueItemExtraData|currentTrackId|extraData=|LegendListRenderItemPropsWithExtraData|renderItem=" apps/mobile/src/components/modals/PlayerQueueModal.tsx

Repository: bbplayer-app/BBPlayer

Length of output: 1881


renderItem 移到组件内部,通过闭包直接读取 currentTrackId,而非通过 extraData 传递。

当前实现违反编码规范。根据指南要求,LegendList 组件的 renderItem 应定义为具名函数,且组件应直接读取可变状态而不依赖 extraData。现在 currentTrackId(可变状态)经由 extraData 传递(第 179-186 行),违反了此规范。

建议将 renderItem 移入 PlayerQueueModal 组件内,用 useCallback 包裹,直接从闭包捕获 currentTrackIdswitchTrackHandlerremoveTrackHandler,并移除 currentTrackIdextraData 中,或完全删除 extraData 依赖:

示意修改
-type QueueItemExtraData = {
-  currentTrackId?: string
-  switchTrackHandler: (index: number) => Promise<void>
-  removeTrackHandler: (index: number) => Promise<void>
-}
-
-const renderItem = ({
-  item,
-  index,
-  extraData,
-}: LegendListRenderItemPropsWithExtraData<OrpheusTrack, QueueItemExtraData>) => {
-  if (!extraData) throw new Error('Extradata 不存在')
-  return (
-    <TrackItem
-      track={item}
-      onSwitchTrack={extraData.switchTrackHandler}
-      onRemoveTrack={extraData.removeTrackHandler}
-      isCurrentTrack={item.id === extraData.currentTrackId}
-      index={index}
-    />
-  )
-}
+const renderQueueItem = useCallback(
+  function renderQueueItem({
+    item,
+    index,
+  }: LegendListRenderItemPropsWithExtraData<OrpheusTrack, undefined>) {
+    return (
+      <TrackItem
+        track={item}
+        onSwitchTrack={switchTrackHandler}
+        onRemoveTrack={removeTrackHandler}
+        isCurrentTrack={item.id === currentTrackId}
+        index={index}
+      />
+    )
+  },
+  [currentTrackId, removeTrackHandler, switchTrackHandler],
+)
...
-<LegendList
+<LegendList
   ref={flatListRef}
   data={queue}
-  renderItem={renderItem}
-  extraData={extraData}
+  renderItem={renderQueueItem}
   keyExtractor={keyExtractor}
   estimatedItemSize={72}

另见第 110-128、179-186、256-261 行。

🤖 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 `@apps/mobile/src/components/modals/PlayerQueueModal.tsx` around lines 104 -
108, Move the LegendList renderItem into the PlayerQueueModal component and stop
passing currentTrackId via extraData: define a named renderItem function inside
PlayerQueueModal (e.g., const renderItem = useCallback(( {item, index} ) => {
... }, [currentTrackId, switchTrackHandler, removeTrackHandler])) so it captures
currentTrackId, switchTrackHandler and removeTrackHandler from the closure;
update the LegendList usage to reference this named renderItem and remove
currentTrackId from the QueueItemExtraData/extraData payload (or eliminate
extraData entirely) and ensure all references to renderItem and extraData
(previously at the LegendList call) are updated accordingly.

@coderabbitai

coderabbitai Bot commented May 19, 2026

Copy link
Copy Markdown

@roitium: Pausing automatic reviews for this PR.

✅ Actions performed

Reviews paused.

@lumigj

lumigj commented Jun 25, 2026

Copy link
Copy Markdown

LGTM 姐姐你的开发好孤独啊为什么只有bot和agent在回复你

@roitium

roitium commented Jun 25, 2026

Copy link
Copy Markdown
Collaborator Author

LGTM 姐姐你的开发好孤独啊为什么只有bot和agent在回复你

????我哭了你不许说话了

@lumigj

lumigj commented Jun 25, 2026

Copy link
Copy Markdown

LGTM 姐姐你的开发好孤独啊为什么只有bot和agent在回复你

????我哭了你不许说话了

不是有codex和copilot陪你吗

@roitium

roitium commented Jun 26, 2026

Copy link
Copy Markdown
Collaborator Author

LGTM 姐姐你的开发好孤独啊为什么只有bot和agent在回复你

????我哭了你不许说话了

不是有codex和copilot陪你吗

少废话,给我赞助个 codex😋

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

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants