Skip to content

fix: resolve scroll key against latest data - #376

Merged
zombieJ merged 3 commits into
masterfrom
agent/fix-scroll-to-key-retry
Jul 31, 2026
Merged

fix: resolve scroll key against latest data#376
zombieJ merged 3 commits into
masterfrom
agent/fix-scroll-to-key-retry

Conversation

@zombieJ

@zombieJ zombieJ commented Jul 30, 2026

Copy link
Copy Markdown
Member

Summary

Resolve scrollTo({ key }) against the latest list data during scroll retries.

Root cause

The key was converted to an index when scrollTo was called. If the same update added the target item, retries kept using the stale -1 index and could not scroll to it.

Changes

  • Preserve key-based scroll targets in retry state
  • Resolve the key from the latest data while its index is unavailable
  • Cache the resolved index for subsequent measurement retries
  • Add a regression test for scrolling while data expands

Test

  • ut test -- --runInBand
  • ut run tsc
  • ut run lint -- src/hooks/useScrollTo.tsx tests/scroll.test.js

Related to ant-design/ant-design#58841.

Summary by CodeRabbit

  • 新功能

    • 滚动定位支持通过唯一键保存并恢复目标位置。
    • 数据更新后,可按指定键重新定位到对应内容。
  • 问题修复

    • 优化滚动同步逻辑,确保目标数据加载后正确计算并恢复滚动位置。
    • 改进滚动请求状态管理,提升重试和定位过程的稳定性。
  • 测试

    • 新增数据扩展后按键定位滚动位置的验证。

@gemini-code-assist

Copy link
Copy Markdown

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@vercel

vercel Bot commented Jul 30, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
virtual-list Ready Ready Preview Jul 31, 2026 3:23am

@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

滚动同步状态新增可选 key。数据更新后,逻辑可通过 getKey 重新解析目标索引,并在索引有效时执行定位。测试覆盖列表扩展后的按 key 滚动行为。

Changes

按 key 同步滚动

Layer / File(s) Summary
滚动请求状态记录
src/hooks/useScrollTo.tsx
同步状态新增 key 字段。对象形式的滚动请求保存按 key 或按 index 指定的目标信息。
同步解析与定位
src/hooks/useScrollTo.tsx, tests/scroll.test.js
同步时在 index 无效时通过 getKey 解析最新索引。仅当容器有高度且索引有效时执行定位。重试状态基于前一状态合并更新。测试验证数据扩展后按 key 30 滚动到 scrollTop 600。

Estimated code review effort: 3 (Moderate) | ~20 minutes

Suggested reviewers: aojunhao123

Poem

兔子抱着滚动条,
key 指向新位置。
数据长高一大截,
索引重新找回家。
轻轻一跳到六百。

🚥 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 标题准确概括了按最新数据解析滚动 key 的主要修复,内容清晰且具体。
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.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch agent/fix-scroll-to-key-retry

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.

@github-actions

github-actions Bot commented Jul 30, 2026

Copy link
Copy Markdown

React Doctor found no issues. 🎉

⚠️ Warning: .github/workflows/react-doctor.yml is configured incorrectly. See below to fix.

React Doctor compares against master to report only the issues this pull request introduces. This run couldn't complete that comparison (usually a shallow CI checkout with no merge base), so it listed every issue in the changed files, including ones that already existed on master.

Add fetch-depth: 0 to the actions/checkout step in .github/workflows/react-doctor.yml so the checkout includes the history React Doctor needs:

 jobs:
   react-doctor:
     steps:
       - uses: actions/checkout@v5
+        with:
+          fetch-depth: 0

       - uses: millionco/react-doctor@v2

To silence this warning, set silence-missing-baseline-warning: true on the React Doctor action.

Reviewed by React Doctor for commit 5475c99.

@github-actions

github-actions Bot commented Jul 30, 2026

Copy link
Copy Markdown

❌ Deploy failed

PR preview ❌ Failed ❌ Failed
🔗 Preview https://react-component-virtual-list-preview-pr-376.surge.sh (may be unavailable)
📝 Commit5475c99
🪵 LogsView logs
📋 Build log (last lines)
npm error
npm error Could not resolve dependency:
npm error peer eslint@"^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" from eslint-plugin-react@7.37.5
npm error node_modules/eslint-plugin-react
npm error   dev eslint-plugin-react@"^7.37.5" from the root project
npm error   eslint-plugin-react@"^7.32.2" from @umijs/fabric@4.0.1
npm error   node_modules/@umijs/fabric
npm error     @umijs/fabric@"^4.0.0" from rc-test@7.1.3
npm error     node_modules/rc-test
npm error       dev rc-test@"^7.1.3" from the root project
npm error
npm error Conflicting peer dependency: eslint@9.39.5
npm error node_modules/eslint
npm error   peer eslint@"^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" from eslint-plugin-react@7.37.5
npm error   node_modules/eslint-plugin-react
npm error     dev eslint-plugin-react@"^7.37.5" from the root project
npm error     eslint-plugin-react@"^7.32.2" from @umijs/fabric@4.0.1
npm error     node_modules/@umijs/fabric
npm error       @umijs/fabric@"^4.0.0" from rc-test@7.1.3
npm error       node_modules/rc-test
npm error         dev rc-test@"^7.1.3" from the root project
npm error
npm error Fix the upstream dependency conflict, or retry
npm error this command with --force or --legacy-peer-deps
npm error to accept an incorrect (and potentially broken) dependency resolution.
npm error
npm error
npm error For a full report see:
npm error /home/runner/.npm/_logs/2026-07-31T03_24_50_317Z-eresolve-report.txt
npm error A complete log of this run can be found in: /home/runner/.npm/_logs/2026-07-31T03_24_50_317Z-debug-0.log

🤖 Powered by surge-preview

@codecov

codecov Bot commented Jul 30, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 96.88%. Comparing base (3153dea) to head (5475c99).
⚠️ Report is 1 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master     #376      +/-   ##
==========================================
+ Coverage   96.87%   96.88%   +0.01%     
==========================================
  Files          19       19              
  Lines         832      836       +4     
  Branches      206      205       -1     
==========================================
+ Hits          806      810       +4     
  Misses         26       26              

☔ View full report in Codecov by Harness.
📢 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.

Comment thread src/hooks/useScrollTo.tsx Outdated
const { targetAlign, originAlign, index, offset: rawOffset } = syncState;
const { targetAlign, originAlign, offset: rawOffset } = syncState;
const index =
syncState.index < 0 && 'key' in syncState

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

逻辑应该是 index >= 0 就使用 index,而不需要关注 key 是否存在。只有 index 不合法才需要去查找 index。

Comment thread src/hooks/useScrollTo.tsx Outdated

const height = containerRef.current.clientHeight;
let needCollectHeight = false;
let needCollectHeight = 'key' in syncState && index < 0;

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

这里保持 false

Comment thread src/hooks/useScrollTo.tsx Outdated
}

const { offset: rawOffset = 0 } = arg;
const target = 'index' in arg ? { index: arg.index } : { index: -1, key: arg.key };

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

不需要这个,你下面还是塞 index 和 key。上面让它自行判断是否 index 合法就行了。

@gemini-code-assist

Copy link
Copy Markdown

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@zombieJ
zombieJ merged commit bba5f62 into master Jul 31, 2026
12 of 13 checks passed
@zombieJ
zombieJ deleted the agent/fix-scroll-to-key-retry branch July 31, 2026 03:41

@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: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/hooks/useScrollTo.tsx (1)

99-105: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

同时校验 index 的上界。

index >= 0 不能证明索引仍属于当前 data。当列表缩短后,缓存索引可能满足 index >= data.length。此时 maxLen 会把遍历截到最后一项,itemTopitemBottom 会对应错误的目标。空列表也会进入定位分支。请把 index < data.length 纳入有效性判断;如果存在 syncState.key,对越界索引重新执行 getKey 查找。

建议修改
       const index =
-        syncState.index >= 0
+        syncState.index >= 0 && syncState.index < data.length
           ? syncState.index
-          : data.findIndex((item) => getKey(item) === syncState.key);
+          : syncState.key !== undefined
+            ? data.findIndex((item) => getKey(item) === syncState.key)
+            : -1;
...
-      if (height && index >= 0) {
+      if (height && index >= 0 && index < data.length) {
🤖 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 `@src/hooks/useScrollTo.tsx` around lines 99 - 105, Update the scroll-target
validity check in the visible scroll handling logic to require index <
data.length alongside height and index >= 0, preventing empty or shortened lists
from entering the positioning path. When syncState.key is present and the cached
index is out of bounds, rerun getKey to resolve the target index before
calculating maxLen, itemTop, and itemBottom.
🤖 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 `@src/hooks/useScrollTo.tsx`:
- Around line 85-89: 让 useScrollTo 中解析 syncState.index 的同步 effect 将 data
加入依赖数组,使数据更新后重新查找目标并执行滚动。保持现有 syncState 与容器引用依赖不变,并为目标跨两个 render 才出现在 data
中的场景补充回归测试。

---

Outside diff comments:
In `@src/hooks/useScrollTo.tsx`:
- Around line 99-105: Update the scroll-target validity check in the visible
scroll handling logic to require index < data.length alongside height and index
>= 0, preventing empty or shortened lists from entering the positioning path.
When syncState.key is present and the cached index is out of bounds, rerun
getKey to resolve the target index before calculating maxLen, itemTop, and
itemBottom.
🪄 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 Plus

Run ID: f88adea6-e578-4140-b8f8-600d544744ce

📥 Commits

Reviewing files that changed from the base of the PR and between 3153dea and 5475c99.

📒 Files selected for processing (2)
  • src/hooks/useScrollTo.tsx
  • tests/scroll.test.js

Comment thread src/hooks/useScrollTo.tsx
Comment on lines +85 to +89
const { targetAlign, originAlign, offset: rawOffset } = syncState;
const index =
syncState.index >= 0
? syncState.index
: data.findIndex((item) => getKey(item) === syncState.key);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

请让同步 effect 在 data 变化时重新运行。

index 的解析依赖当前 data,但该 effect 的依赖数组没有 data。如果先调用 scrollTo({ key: '30' }),目标尚不存在,第一次 effect 会保存 index = -1 并结束。之后列表在另一次 render 中加入目标项时,syncState 和容器引用都不变,effect 不会再次执行,滚动请求会永久停留在 -1。请将 data 加入依赖数组,并补充跨两个 render 的回归测试。

建议修改
-  }, [syncState, containerRef.current]);
+  }, [syncState, data, containerRef.current]);
🤖 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 `@src/hooks/useScrollTo.tsx` around lines 85 - 89, 让 useScrollTo 中解析
syncState.index 的同步 effect 将 data 加入依赖数组,使数据更新后重新查找目标并执行滚动。保持现有 syncState
与容器引用依赖不变,并为目标跨两个 render 才出现在 data 中的场景补充回归测试。

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.

1 participant